Search Documentation
Search across all documentation pages
Go SDK

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-go

Import 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, &notFound):
        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)
    }
}
_ = job

Every 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:

OptionDefaultNotes
WithBaseURL(url)https://api.transcodely.comOverride for staging or self-hosted
WithHTTPClient(c)&http.Client{Timeout: 60s}Inject a custom transport
WithMaxRetries(n)2Retries 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 timeSent as Transcodely-Version
WithAutoIdempotency(b)trueAuto-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).