Go SDK
github.com/transcodely/transcodely-go is the official Go SDK for the Transcodely API. It requires Go 1.23+, builds on connectrpc.com/connect v1, and ships a custom JSON codec so the SDK speaks the API’s snake_case + lowercase-enum wire format identically to the TypeScript and Python SDKs. The SDK is alpha (0.1.0) — breaking changes are possible on minor bumps until 1.0.0.
Install
go get github.com/transcodely/transcodely-goImport as the transcodely package:
import "github.com/transcodely/transcodely-go"Authenticate
transcodely.New takes the API key as the first positional argument followed by zero or more functional options. Pass os.Getenv("TRANSCODELY_API_KEY") so the calling code stays in control of secret loading — the SDK does not read environment variables itself.
package main
import (
"log"
"os"
"github.com/transcodely/transcodely-go"
)
func main() {
client, err := transcodely.New(os.Getenv("TRANSCODELY_API_KEY"))
if err != nil {
log.Fatal(err)
}
_ = client
}transcodely.New returns an error only on invalid configuration (e.g. an empty API key). Test (ak_test_*) and live (ak_live_*) keys hit the same base URL — the environment is encoded in the prefix.
Create your first job
Resource namespaces hang off the root client (client.Jobs, client.Videos, …). Every RPC takes a context.Context and a typed *Params struct. Optional *string fields use proto.String(...) from google.golang.org/protobuf/proto.
package main
import (
"context"
"log"
"os"
"github.com/transcodely/transcodely-go"
"google.golang.org/protobuf/proto"
)
func main() {
client, err := transcodely.New(os.Getenv("TRANSCODELY_API_KEY"))
if err != nil {
log.Fatal(err)
}
job, err := client.Jobs.Create(context.Background(), &transcodely.JobCreateParams{
InputUrl: "https://storage.example.com/source.mp4",
OutputOriginId: proto.String("ori_x9y8z7w6v5"),
Outputs: []*transcodely.OutputSpec{{
Type: transcodely.OutputFormatHLS,
Video: []*transcodely.VideoVariant{
{Codec: transcodely.VideoCodecH264, Resolution: transcodely.Resolution1080P},
{Codec: transcodely.VideoCodecH264, Resolution: transcodely.Resolution720P},
},
}},
})
if err != nil {
log.Fatal(err)
}
log.Printf("created %s in status %s", job.GetId(), job.GetStatus())
}Generated message accessors (job.GetId(), job.GetStatus()) are the safe way to read fields — they handle nil pointers correctly. Field names follow the proto convention (InputUrl, not InputURL).
Watch a job to completion
client.Jobs.Watch returns a *Stream[T] that auto-reconnects on transient network failures and filters HEARTBEAT events by default.
import (
"context"
"log"
"github.com/transcodely/transcodely-go"
)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()
stream := client.Jobs.Watch(ctx, job.GetId())
defer stream.Close()
for stream.Next() {
event := stream.Current()
j := event.GetJob()
log.Printf("[%s] progress=%d%%", j.GetStatus(), j.GetProgress())
if j.GetStatus() == transcodely.JobStatusCompleted ||
j.GetStatus() == transcodely.JobStatusFailed ||
j.GetStatus() == transcodely.JobStatusCanceled {
break
}
}
if err := stream.Err(); err != nil {
log.Fatal(err)
}Cancel the parent context.Context (or call stream.Close()) to terminate the stream from your side. Every reconnect re-emits a SNAPSHOT event so resuming is idempotent on the consumer.
List with auto-pagination
List methods return a *Iter[T]. Drive it with Next() / Current() / Err(), and always defer iter.Close() to release the underlying HTTP connection.
iter := client.Jobs.List(ctx, &transcodely.JobListParams{
Pagination: &transcodely.PaginationRequest{Limit: 50},
})
defer iter.Close()
for iter.Next() {
job := iter.Current()
log.Printf("%s %s", job.GetId(), job.GetStatus())
}
if err := iter.Err(); err != nil {
log.Fatal(err)
}The iterator stops cleanly when the API returns an empty next_cursor. See Pagination for cursor semantics.
Handle typed errors
Every concrete error implements the transcodely.Error interface (with ErrorCode() and RequestID() methods). Match types with errors.As:
import (
"errors"
"log"
"time"
"github.com/transcodely/transcodely-go"
)
job, err := client.Jobs.Get(ctx, "job_does_not_exist")
if err != nil {
var notFound *transcodely.NotFoundError
var invalid *transcodely.InvalidRequestError
var rate *transcodely.RateLimitError
var auth *transcodely.AuthenticationError
switch {
case errors.As(err, ¬Found):
log.Printf("not found, request_id=%s", notFound.RequestID())
case errors.As(err, &invalid):
for _, v := range invalid.Errors() {
log.Printf("%s: %s", v.Field, v.Description)
}
case errors.As(err, &rate):
time.Sleep(rate.RetryAfter)
case errors.As(err, &auth):
log.Fatal("bad API key")
default:
log.Fatal(err)
}
}
_ = jobEvery error carries ErrorCode(), HTTPStatus(), RequestID(), and the raw response body. See Errors for the full hierarchy.
Override idempotency
Create mutations auto-generate a UUID v4 Idempotency-Key so retrying within the same process is safe. For cross-process safety (queue workers, cron jobs), set IdempotencyKey on the params struct:
import "google.golang.org/protobuf/proto"
params := &transcodely.JobCreateParams{
InputUrl: "...",
OutputOriginId: proto.String("ori_x9y8z7w6v5"),
Outputs: []*transcodely.OutputSpec{/* ... */},
IdempotencyKey: proto.String("transcode_asset_42_v1"),
}
job, err := client.Jobs.Create(ctx, params)To disable auto-injection across the entire client (e.g. when targeting a server that doesn’t yet support idempotency keys), use WithAutoIdempotency(false) at construction. See Idempotency for replay semantics.
Configuration
Pass options to transcodely.New:
| Option | Default | Notes |
|---|---|---|
WithBaseURL(url) | https://api.transcodely.com | Override for staging or self-hosted |
WithHTTPClient(c) | &http.Client{Timeout: 60s} | Inject a custom transport |
WithMaxRetries(n) | 2 | Retries on network errors, 5xx, 429, 503 with jittered backoff |
WithUserAgent(ua) | — | Appended to the default transcodely-go/<version> |
WithAPIVersion(v) | calendar version baked at SDK build time | Sent as Transcodely-Version |
WithAutoIdempotency(b) | true | Auto-generate UUIDv4 Idempotency-Key on Create mutations |
Note that the Go SDK defaults MaxRetries to 2, where the TypeScript and Python SDKs default to 3. All three back off with jitter and honor the server’s Retry-After header.
client, err := transcodely.New(
os.Getenv("TRANSCODELY_API_KEY"),
transcodely.WithMaxRetries(5),
transcodely.WithUserAgent("ingest-worker/2.3"),
transcodely.WithHTTPClient(&http.Client{Timeout: 90 * time.Second}),
)Where to go next
- API Reference — the full RPC surface.
- Webhook Integration — verify signed deliveries and handle events with
transcodely.ConstructEvent. - Errors — the typed error hierarchy in one place.
- Pagination — cursor model and auto-paging idioms.
- Idempotency — when and how to set your own key.
- SDK source on GitHub — full source and four runnable examples in
examples/(01_create_job,02_watch_job,03_pagination,04_error_handling).