Platform

Should I use a Bird SDK or call the API directly?

This is not a question about capability. Bird's API is a plain HTTP API and calling it with any client is fully supported, so the SDKs are not a privileged path. What they are is a pre-written request lifecycle, and the decision is whether you would rather write that yourself.

It is also less of a fork than it looks, for a reason worth knowing before you choose: an SDK does not have to have a typed method for an operation to call it.

What do the SDKs actually do for me?

Five things, each of which is a thing you would otherwise build.

Auto-idempotency. Every mutating call gets a generated Idempotency-Key, made once per logical call and reused across retries, so a write that times out after the server processed it returns the stored response instead of applying twice.

Retries that are safe by construction. Retries are on by default with two attempts after the first. The client retries network errors, per-attempt timeouts, 429s and retryable 5xxs, using jittered exponential backoff and honouring Retry-After. It never retries a deterministic failure: a 401, 404, 422 or other 4xx is returned to you rather than repeated. The idempotency key is what makes retrying a mutation safe rather than reckless.

Cursor pagination. List endpoints are cursor-paginated, and every SDK offers native iteration that fetches successive pages as you walk them, plus a single-page accessor when you want the cursor yourself.

Region routing. A Bird key encodes its region in the prefix, and the client reads it and routes to the matching regional host without configuration.

Webhook verification. One entry point, webhooks.unwrap(rawBody, headers), implementing Standard Webhooks with the signature check, the five-minute timestamp tolerance and constant-time comparison, returning an event typed on its type.

Which languages have a REST SDK?

Four: TypeScript, Python, Go and PHP.

There is a distinction worth getting right, because the SDK directory lists more than four. The Swift and Kotlin packages are Realtime clients, hand-written WebSocket packages that receive events, and they deliberately share none of the behaviour above: no retry model, no idempotency, no pagination, because none of it applies to a subscription. The Kotlin one is a plain JVM library rather than an Android-only one, so a Java caller can take it, and it is still not a REST client. On any of those platforms, calling the REST API means calling it directly or generating your own client.

When is calling the API directly the right choice?

Four cases, and only one of them is a compromise.

Your language has no REST SDK. Ruby, Rust, C#, Java, Elixir: the API is the API, and the OpenAPI specification is published, so you can generate a typed client rather than hand-rolling requests.

You want to generate your own client. The SDKs are themselves generated from that specification and then hand-wrapped. If your organisation standardises on its own generator and conventions, you are doing the same thing we did, from the same source.

Your runtime will not take the dependency. A constrained environment, a serverless bundle you are keeping small, or a policy against third-party packages in a payment path.

You are exploring. curl against the reference is often the fastest way to understand a shape before you commit to a library.

What do I have to build myself then?

The five things above, and it is worth being concrete because two of them are easy to get subtly wrong.

Generating and reusing an idempotency key per logical operation, not per HTTP attempt, is the one people get backwards: a fresh key on each retry defeats the mechanism entirely. And retrying the right failures is the other, because retrying a 422 never succeeds and retrying a 5xx without a key can duplicate a send.

You also need to read the region off your key prefix and pick the host, walk cursors rather than assuming offsets, and verify webhook signatures over the raw body bytes.

None of that is hard. It is just already written, in four languages, against the same specification.

What if the SDK has no method for what I need?

Use its verb methods, and you keep everything above.

The typed surface is deliberately a subset rather than a wrapper around every operation: each exported method is a public commitment, so the curated layer covers what most callers need and the long tail is reached through the SDK's own get, post and equivalent verb methods. The gap is not marginal. WhatsApp, for instance, carries 43 operations in the surface catalogue and five of them are on the typed SDK surface.

That matters because it dissolves the choice this page is about. Calling an operation through a verb method is raw HTTP against the same endpoint you would have called yourself, with the region routing, the idempotency key, the retry policy and the timeout already wired. So you rarely have to pick between the SDK and the API: you pick the SDK, and drop to its verb methods where the typed surface stops.

What surprises people about the SDKs?

Four behaviours worth knowing before you debug one.

Construction fails on a malformed key. Because the client infers the region from the bk_{region}_ prefix, a key that does not match that shape fails at construction unless you set a region or base URL explicitly. If you build the client at boot, that surfaces as a startup error naming the cause rather than as a puzzling 401 later. Construct it lazily or per invocation, as a serverless handler often does, and you get the same error at the first call instead.

The timeout is per attempt. Sixty seconds by default, with two retries, so a call that exhausts its retries can take substantially longer than the timeout you set. If you have an outer deadline, set maxRetries with it in mind.

PHP is the exception on timeouts. It uses the timeout enforced by the HTTP client you inject, because PSR-18 has no portable per-request timeout.

SDK-owned headers win. Authorization, User-Agent and Idempotency-Key take precedence over headers you pass, so if you want your own idempotency key, pass it through the option rather than as a raw header.

One more, for a case the default does not cover: auto-idempotency keys one SDK call. If your logical operation is an application-level retry loop around the SDK, each pass generates a new key and the protection is gone. Pass your own key for the operation so every pass shares it.

SDK concepts has the shared behaviour in full, and SDKs lists each package with its minimum runtime version.

Build on the same network.

A test API key is yours immediately. Production unlocks when you add a payment method and verify a sender.

Start with one channel.
Add the others when you're ready.

A test API key is yours immediately. Production unlocks when you add a payment method and verify a sender.

Using Claude Code, Cursor, or Codex? Copy a setup prompt and your agent installs the Bird CLI and skills for you. Pick yours:

Cursor