Skip to content

Authentication and keys

Every call to the API, except GET /v1/status and GET /v1/pricing, needs an API key.

Make keys in the portal under API keys. You need the owner, admin or developer role, and two-factor authentication.

  • Test keys start with ac_test_. They are free and never reach a real source; see test mode. You get one when you sign up.
  • Live keys start with ac_live_. They make real calls on your credit. You can make them once we have verified your company.

A key is shown in full once, when you make it. After that the portal only shows its first 12 characters. Store it as a secret, for example in an environment variable, and never in code that runs in a browser or an app. An account can have 25 keys that are not revoked or expired.

Send the key as a Bearer token:

Terminal window
curl https://api.accountcloud.io/v1/me \
-H "Authorization: Bearer ac_test_…"

GET /v1/me is free and needs no scope, which makes it a good first call:

{
"account": { "id": "acc_01jbxk3v6c9m2q8r4t5w7y0z1a", "name": "Example B.V.", "status": "active" },
"environment": "test",
"api_key": { "id": "key_01jbxk3v6c9m2q8r4t5w7y0z1a", "name": "Default test key", "prefix": "ac_test_Xb3k", "expires_at": null },
"scopes": ["*"],
"balance": { "amount": "12.50", "currency": "EUR" }
}

A missing, unknown, revoked or expired key gets a 401 (unauthenticated, invalid_api_key, api_key_expired) with WWW-Authenticate: Bearer.

When you make or change a key, you set:

  • Scopes. * for everything, also what we add later. Or a list of products (address) and single operations (address.forms). A product scope covers every operation of that product. Without the scope, a call gets 403 insufficient_scope, before its input is checked. GET /v1/me needs no scope.
  • An IP allowlist. IPv4 and IPv6 addresses or CIDR blocks, such as 203.0.113.10 or 198.51.100.0/24. Empty means any address. A call from elsewhere gets 403 ip_not_allowed.
  • A monthly spending cap, in euros per calendar month (UTC). A live call that would pass it gets 429 monthly_cap_reached.
  • An end date. After it, the key gets 401 api_key_expired.

Rotate a key to get a new one with the same name, scopes and limits. The old key keeps working for the overlap you choose: up to 7 days, 24 hours by default, or not at all. During the overlap, every answer to the old key carries Deprecation: true and a Sunset header with the time it stops. After that it gets api_key_expired.

Revoke a key when it may have leaked: it stops at once, with invalid_api_key.

Per key, per minute Per account, per UTC day
Live 600 250,000
Test 60 10,000

The minute is a fixed window per key. The day counts all keys of the account in that environment and resets at 00:00 UTC. We can raise the limits for your account; ask support.

Every answer to a request that passed the key and scope checks says where you stand, in the RateLimit-Policy and RateLimit headers:

RateLimit-Policy: "minute";q=600;w=60, "day";q=250000;w=86400
RateLimit: "minute";r=599;t=60, "day";r=249999;t=43200

q is the quota and w the window in seconds; r is what is left and t the seconds until that window resets.

When a window is full you get 429 with Retry-After: rate_limit_exceeded for the minute, daily_quota_exceeded for the day. A refused request does not count, so waiting for Retry-After is enough.