Docs / Developer API
Keys and limits
No key is needed for anything. A key raises your limits and gives you usage stats.
Keys
A key gives you higher limits and usage stats; it is never needed to plant or trade, and a leaked key moves no funds. Send it in the Authorization header, never in a URL, where logs and browser history keep it.
curl "https://api.sapling.cash/v1/stats" -H "Authorization: Bearer sap_…"- Make a key by signing in with your Solana wallet: one signed message, no funds move. Up to 5 keys per wallet.
- A key is shown once, when it is made. Sapling stores a hash of it and its first 12 characters (to tell your keys apart), so a lost key cannot be shown again: make a new one.
- To rotate, make a new key, switch to it, then revoke the old one. A revoked key stops working within 10 seconds.
- A key that does not exist or was revoked is refused (401). Leave the header out to use the anonymous limits.
Limits
Anonymous requests count per IP address; keyed requests count per key.
| Anonymous | Key: standard | Key: raised | |
|---|---|---|---|
| Reads | 60 / min | 600 / min | 3,000 / min |
| Quotes | 30 / min | 300 / min | 1,200 / min |
| Trade, claim, swap and payee builds | 10 / min | 120 / min | 600 / min |
| Relay sends | 10 / min | 120 / min | 600 / min |
| Plant uploads | 3 / hour | 30 / hour | 120 / hour |
| Plant builds | 3 / hour | 30 / hour | 120 / hour |
Every key starts standard. Raised limits are set by hand for integrations that need them: ask on Sapling's official channels, listed in the docs.
Plants need no key. What counts is the wallet's own signature on the planting. A key raises only the per-IP rows. No wallet is limited in how many coins it plants: these limits protect this service, and planting on sapling.cash never counts against them.
Headers
X-RateLimit-Limit: the requests your tier allows in the window of this route.X-RateLimit-Remaining: what is left of it.Retry-After: on a 429, the seconds to wait before trying again.
Errors
An error answers a status and one body, the same everywhere:
{
"error": {
"code": "rate_limited",
"message": "Too many requests. Try again in 12 seconds."
}
}The code is stable: branch on it. The message is plain words for a person and may change.
| invalid_param | 400 | A parameter or a field of the body is missing or not in the right form. The message names it. |
| unauthorized | 401 | The Authorization header holds a key that does not exist or was revoked. Send no key to use the anonymous limits. |
| planting_closed | 403 | Planting is closed for now. |
| not_found | 404 | No such coin, run, build or transaction. A hidden coin answers exactly like a missing one. |
| image_in_review | 409 | The image needs a person to look at it first. Nothing was published: no pin, no metadata URI. Upload the same image again after the review, or choose another one. |
| image_refused | 422 | The image is not allowed on Sapling (explicit content). |
| refused | 422 | The transaction was not built, or not sent: its simulation failed, or it did not pass the check. The message says why. |
| rate_limited | 429 | Too many requests for your tier. Wait the seconds in Retry-After. |
| unavailable | 503 | Solana or the index could not answer in time. Try again. |
Inside /v1 only additions are made: new fields, new endpoints. Ignore fields you do not know.