Go SDK for image and video uploads
dreep-go is the official Go SDK for Dreep. Install it with go get github.com/IndigoSoftwares21/dreep-go on Go 1.22 or later. It uses only the standard library, streams multipart uploads instead of buffering them, presigns direct uploads for browsers, builds transform and signed URLs locally, and retries safe requests on 429 and 5xx responses. Every request method takes a context.Context first.
Install and run your first upload
Create an API key on the API Keys page of the dashboard and keep it in an environment variable on your server. dreep.New returns an error if the key is empty. The cURL tab shows the request Upload sends.
client, err := dreep.New(os.Getenv("DREEP_API_KEY"))
if err != nil {
log.Fatal(err)
}
f, err := os.Open("hero.jpg")
if err != nil {
log.Fatal(err)
}
defer f.Close()
fi, _ := f.Stat()
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
asset, err := client.Upload(ctx, dreep.UploadOptions{
File: f,
Filename: fi.Name(),
KnownSize: fi.Size(), // exact Content-Length, no chunked encoding
Destination: dreep.Destination{Folder: "marketing/2026"},
Transform: &dreep.Transform{Width: 1200, Format: dreep.FormatWebP, Quality: 80},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(asset.URL)
fmt.Println(client.URL(asset.ID, &dreep.Transform{Width: 400})) // no network callcurl -X POST https://api.dreep.cloud/api/v1/upload \
-H "Authorization: Bearer $DREEP_API_KEY" \
-F "file=@hero.jpg" \
-F "folder=marketing/2026" \
-F "width=1200" \
-F "format=webp" \
-F "quality=80"What the package gives you
A small client that behaves like the rest of your Go code: contexts for deadlines, plain structs for options, one error type you can inspect with errors.As.
Standard library only
net/http, mime/multipart and crypto/hmac. Nothing is added to go.sum beyond the module itself, which keeps audits and upgrades simple.
Streaming uploads
Upload writes the multipart body through an io.Pipe, so a 400 MB video is never held in memory. Set KnownSize for an exact Content-Length.
Context first, safe to share
Every request method takes a context.Context, so deadlines and cancellation work as usual. One *dreep.Client is safe for concurrent use across goroutines.
Retries that can't duplicate
Requests with replayable bodies are retried on 429 and 5xx. Streaming uploads are never retried, so a failed upload can't store the same file twice.
Two ways to upload
Upload sends the file through the Dreep API in one request. UploadPresigned presigns, PUTs the bytes straight to Dreep storage and confirms, in one call. Both accept files up to 500 MB.
| Behaviour | Upload | UploadPresigned |
|---|---|---|
| Requests | One multipart POST | Presign, PUT, confirm |
| Where the bytes go | Through the Dreep API | Straight to Dreep storage |
| Transform | Before storage | At confirm |
| Duplicate detection | No | ContentHash skips the PUT when Dreep already has the bytes |
| Automatic retries | Never | Confirm retried on 409 while bytes settle |
Video uploads and trimmed clips
Video inputs are MP4, MOV and WebM. Upload the original once, then request trimmed, resized or re-encoded variants by URL. Light jobs return straight away; heavy ones such as a re-encode return 202 while processing, so request the same URL again until it returns 200 from cache.
fi, err := f.Stat()
if err != nil {
return err
}
asset, err := client.UploadPresigned(ctx, dreep.DirectUploadOptions{
PresignOptions: dreep.PresignOptions{
ContentType: "video/mp4",
SizeBytes: fi.Size(),
Filename: "launch.mp4",
Destination: dreep.Destination{Folder: "videos/2026"},
ContentHash: sha256Hex, // optional: skip the PUT for known bytes
},
File: f,
})
if err != nil {
return err
}
clip := client.URL(asset.ID, &dreep.Transform{
Width: 720,
Format: dreep.FormatMP4,
TrimStart: dreep.Ptr(2.0),
TrimEnd: dreep.Ptr(12.0),
})# Same clip by URL; 202 while processing, then 200 from cache
curl -I "https://cdn.dreep.cloud/api/v1/fetch/<assetId>.mp4?format=mp4&width=720&trimStart=2&trimEnd=12"Video transformations have their own monthly allowance: 20 on Free, 1,000 on Starter and 4,000 on Pro. There are no webhooks; polling the URL is the completion signal.
Browser uploads from an http.Handler
Browsers never hold your API key. Your handler checks who is uploading and returns a single-use upload URL, valid for 10 minutes. The browser PUTs the file there with a matching Content-Type and Content-Length, then calls your confirm route.
Browser asks
name, type and size
Handler presigns
PresignUpload
Browser PUTs
bytes to uploadUrl
Handler confirms
ConfirmUpload
Asset ready
CDN URL returned
The handler and the browser side
Two routes on a Go 1.22 ServeMux. Put your own authentication middleware in front of both, and check the confirm id belongs to an upload that user started.
type presignRequest struct {
Filename string `json:"filename"`
ContentType string `json:"contentType"`
SizeBytes int64 `json:"sizeBytes"`
}
func presignHandler(client *dreep.Client) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Authorise the user and enforce your own quotas first.
var in presignRequest
if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
http.Error(w, "bad request", http.StatusBadRequest)
return
}
upload, err := client.PresignUpload(r.Context(), dreep.PresignOptions{
Filename: in.Filename,
ContentType: in.ContentType,
SizeBytes: in.SizeBytes,
Destination: dreep.Destination{Folder: "user-uploads"},
})
if err != nil {
http.Error(w, "could not start upload", http.StatusBadGateway)
return
}
json.NewEncoder(w).Encode(map[string]string{
"id": upload.ID,
"uploadUrl": upload.UploadURL,
})
})
}
func confirmHandler(client *dreep.Client) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
asset, err := client.ConfirmUpload(r.Context(), r.PathValue("id"), nil)
if err != nil {
http.Error(w, "could not confirm upload", http.StatusBadGateway)
return
}
json.NewEncoder(w).Encode(asset)
})
}
mux := http.NewServeMux()
mux.Handle("POST /uploads", presignHandler(client))
mux.Handle("POST /uploads/{id}/confirm", confirmHandler(client))// Browser: no API key, only the single-use URL
const res = await fetch("/uploads", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ filename: file.name, contentType: file.type, sizeBytes: file.size }),
});
const { id, uploadUrl } = await res.json();
await fetch(uploadUrl, { method: "PUT", body: file, headers: { "Content-Type": file.type } });
await fetch(`/uploads/${id}/confirm`, { method: "POST" });Transform URLs and signed links
URL builds a delivery URL from an asset ID and a *dreep.Transform. It returns a string with no error and makes no request. When Format is set, the extension is added to the path as well as the query. SignedURL adds exp and sig for assets in signed folders, computed locally with your signing secret.
client, err := dreep.New(
os.Getenv("DREEP_API_KEY"),
dreep.WithSigningSecret(os.Getenv("DREEP_SIGNING_SECRET")),
)
card := client.URL(asset.ID, &dreep.Transform{
Width: 1200, Height: 630, Fit: dreep.FitCover,
Gravity: dreep.GravityAttention, Format: dreep.FormatAVIF, Quality: 70,
})
// .../fetch/<id>.avif?fit=cover&format=avif&gravity=attention&height=630&quality=70&width=1200
thumb := client.URL(asset.ID, &dreep.Transform{Preset: "thumbnail"}) // ...?p=thumbnail
signed, err := client.SignedURL(asset.ID, 15*time.Minute, nil)
// ...?exp=1774118400&sig=a3f9e2b1...SignedURL returns dreep.ErrNoSigningSecret if the client was built without WithSigningSecret. Pass nil as the transform: the Go SDK signs only the asset ID and expiry, and Dreep rejects signed links that carry transform parameters signed that way. Use the JavaScript SDK if you need signed, transformed links. The signature is an HMAC-SHA256, so anyone holding the link can fetch it until then: keep TTLs short and issue links from a handler that checks your own permissions.
Retries and errors
Every non-2xx response is a *dreep.Error with StatusCode, Code and Message. Helpers cover the cases worth branching on. Network failures and context cancellation wrap the underlying error, so errors.Is(err, context.DeadlineExceeded) works.
| Helper | Status | True when |
|---|---|---|
| IsUnauthorized | 401 | Bad API key, or a signed asset without a valid exp and sig |
| IsPaymentRequired | 402 | A plan limit was hit: storage, transformations or credits |
| IsNotFound | 404 | Unknown asset, folder, preset or presigned upload |
| IsConflict | 409 | ConfirmUpload ran before the bytes landed |
| IsInvalidRequest | 400 | The API returned the invalid_request code |
| FeatureLimit | 402 | Returns the feature key when the limit error carries detail |
Tuning retries and handling limits
By default, replayable requests get two retries on 429 and 5xx, waiting 500ms and then doubling. A Retry-After header always wins. The default http.Client has no global timeout, so bound calls with a context.
client, err := dreep.New(os.Getenv("DREEP_API_KEY"),
dreep.WithMaxRetries(4), // 0 disables retries
dreep.WithRetryBaseDelay(200*time.Millisecond),
)
asset, err := client.Upload(ctx, opts)
switch {
case err == nil:
return asset.URL, nil
case dreep.IsPaymentRequired(err):
if feature, ok := dreep.FeatureLimit(err); ok {
var e *dreep.Error
errors.As(err, &e)
log.Printf("limit hit on %s: used %d of %d", feature, e.Used.Int64(), e.Limit.Int64())
}
return "", err // upgrade or buy an add-on; there is no automatic overage
case errors.Is(err, context.DeadlineExceeded):
return "", fmt.Errorf("upload took too long: %w", err)
default:
return "", err
}Who this is for
- Go APIs and workers that accept user uploads or generate media, such as thumbnails or exports.
- Services that want no third-party modules in go.sum for their media client.
- Large video uploads from a server, where streaming or presigned PUTs keep memory flat.
- Backends issuing presigned URLs to web or mobile clients from an http.Handler.
When Dreep is not the right fit
- You are on a Go version older than 1.22.
- You want to embed the API key in a CLI or desktop app you ship to users. Keep it on a server.
- You need a video player, live streaming or DRM. Dreep delivers files and HLS manifests; the player is yours.
Frequently asked questions
How do I install the Dreep Go SDK?
Run go get github.com/IndigoSoftwares21/dreep-go and import github.com/IndigoSoftwares21/dreep-go. The package name is dreep. It needs Go 1.22 or later and has no dependencies outside the standard library.
Does Upload load the whole file into memory?
No. Upload streams the multipart body through an io.Pipe, so memory stays flat whatever the file size. Pass KnownSize to send an exact Content-Length instead of chunked transfer encoding.
When should I use UploadPresigned instead of Upload?
Use UploadPresigned for large files or when you want duplicate detection with ContentHash: the bytes go straight to Dreep storage and the confirm step is retried on 409. Use Upload for a simple one-request upload with a transform applied before storage.
Which requests are retried?
Requests with replayable bodies, such as PresignUpload, ListMedia, DeleteMedia and the folder, preset and usage calls, are retried on 429 and 5xx with exponential backoff. Streaming uploads, ConfirmUpload and ExtractText are sent once. Connection failures are returned immediately.
Can I sign URLs without a network call?
Yes. Build the client with dreep.WithSigningSecret, then call client.SignedURL(assetID, ttl, nil). It computes an HMAC-SHA256 signature locally and appends exp and sig to the delivery URL.
Is dreep-go open source?
Yes. It is MIT licensed on GitHub at IndigoSoftwares21/dreep-go, with the API reference on pkg.go.dev and a runnable upload example in the examples folder.
Related
All SDKs
Go and JavaScript side by side, plus REST for every other language.
JavaScript and TypeScript SDK
The same API from Node.js, with typed transforms and ESM or CommonJS.
Video transcoding API
Formats, codecs, trimming, HLS and how processing jobs complete.
Secure file uploads
What Dreep validates on upload and what your backend still owns.
SDK documentation
Every method, option struct and return type in the Go SDK reference.
Source on GitHub
Read the code, run the upload example, open an issue.
Pricing
Plans from $0, with storage, image and video transformation limits.
go get dreep-go and upload your first file
Create a free project for 1 GB of storage, 100 image and 20 video transformations a month, copy an API key, and run the upload example.