Authentication

Authenticate API requests with project-scoped API keys, layered with grants, wallet bindings, and network controls.

SDP layers independent controls onto a single API key — grants, wallet bindings, and IP allowlist — so a key grants exactly the access its integration needs, and no more.

Authenticated API requests carry an API key in the Authorization header:

Terminal
curl https://api.solana.com/v1/wallets \
  -H "Authorization: Bearer sk_test_..."

The Bearer prefix is optional — a raw key in the Authorization header is also accepted.

Key scope

Keys are issued per project and inherit that project's environment: sk_test_ keys belong to sandbox projects (devnet), sk_live_ keys to production projects (mainnet-beta). A key can only reach the wallets, tokens, and payments of the project it was issued for; there is no cross-project or organization-wide key.

Authorization layers

A request must clear every layer that is configured on its key:

LayerWhat it controls
GrantsThe actions (module.verb) the key may perform. Each route needs exactly one; see Grants by module
Wallet bindingsRestrict which custody wallets the key can operate, each binding with its own permission list
IP allowlistOptional IPv4/IPv6/CIDR ranges the key may be used from, set on the key; an organization-wide list under Settings applies to every key on top. Neither restricts signed-in dashboard members

Errors

Authentication and authorization failures are always machine-distinguishable: the code says what kind of refusal it is, and a 403 names what it is missing in error.details.

ConditionStatusCodeerror.details
No API key on a keyed-only request401UNAUTHORIZED
Malformed or unknown key401INVALID_API_KEY
Key has been revoked, its organization was deleted or suspended, or its project was archived401REVOKED_API_KEY
Key past its expiry or rotation deadline401EXPIRED_API_KEY
Request origin not in the key's or the organization's IP allowlist403FORBIDDENreason: "ip_not_allowed"
Route only a signed-in dashboard member may use, such as managing keys, members or the organization403FORBIDDENreason: "human_session_required"
Key lacks the route's action, or the action a signature it asks for needs403INSUFFICIENT_PERMISSIONSaction names the missing action
SDP refused to sign for the request (see below)403FORBIDDENreason names why
Per-key rate limit exceeded429RATE_LIMITED

Signing refusals

SDP decides again each time it signs on a key's behalf, so a change to the key or the organization applies to the next signature, including one later in the same request. Nothing is signed or sent on refusal. error.details.reason is one of:

reasonWhy
wallet_out_of_scopeThe custody wallet is outside the key's wallet scope
wallet_purposeThe custody wallet's purpose may not send payments: only a transfer wallet can be the source of a transfer, batch, recurring payment or offramp
principal_not_liveThe key was revoked, expired, or passed its rotation deadline during the request
organization_inactiveThe organization is suspended or deleted
production_not_enabledThe project is a production project and the organization does not have production access
project_not_foundThe project no longer exists
principal_missingNo credential is attached to the movement

A key without the action a signature needs is refused with INSUFFICIENT_PERMISSIONS and error.details.action, exactly as at the route.

Requests are rate limited per key; the standard tier allows 100 requests per minute.

See Manage API Keys for creating, rotating, and revoking keys.

Is this page helpful?