Understanding errors and rate limits
Every error has the same shape, so your code can branch on error.code instead of reading the message. Messages are written for people and can change. Codes are stable.
{ "error": { "code": "rate_limited", "message": "Rate limit of 100 requests per minute exceeded.", "request_id": "req_4f1c2b7e9a0d4e6f8b1a2c3d4e5f6a7b" }}Error codes
Section titled “Error codes”| HTTP | Code | What it means | What to do |
|---|---|---|---|
| 400 | invalid_request |
A parameter is wrong, unknown, repeated, sent as an array, or combined with one it cannot be combined with. message names the parameter |
Fix the request. Don’t retry it unchanged |
| 400 | invalid_cursor |
The cursor cannot be read | Restart the list from your last saved updated_at |
| 401 | invalid_api_key |
The key is missing, malformed, unknown or revoked | Check the Authorization: Bearer header. Create a new key if it was revoked |
| 403 | plan_not_eligible |
The store’s plan does not include the API | Upgrade to Premium, or contact support |
| 403 | shop_inactive |
Bloop is not installed on the store, or API access is suspended for it | Reinstall Bloop. If it is installed, contact support |
| 403 | insufficient_scope |
The key does not have the scope the endpoint needs | Create a key that has the scope |
| 404 | referrer_not_found |
No referrer matches, the id is not a valid referrer id, or the referrer belongs to another store | Treat the person as “not a referrer” |
| 404 | not_found |
The path, the method or the campaign does not exist, or the campaign belongs to another store | Check the URL, use GET, check campaign_id |
| 429 | rate_limited |
Too many requests for the store, or too many invalid keys from your IP | Wait the number of seconds in Retry-After, then retry |
| 500 | internal_error |
Something failed on Bloop’s side | Retry later with backoff. If it persists, contact support with the request_id |
| 503 | service_unavailable |
Bloop is briefly unable to check the key or read the data | Wait the number of seconds in Retry-After, then retry |
New codes can appear within version 1. Handle a code you don’t know by its HTTP status.
The order of checks
Section titled “The order of checks”Bloop checks a request in this order and stops at the first problem:
- An IP that already received 30
401responses within the current 60-second window gets429, before its key is even read. - The key:
401 invalid_api_key. - The store’s access:
403 plan_not_eligibleor403 shop_inactive. - The rate limit:
429 rate_limited. - The path and method:
404 not_found. - The key’s scope:
403 insufficient_scope. - The parameters:
400 invalid_requestor400 invalid_cursor.
Requests rejected at steps 5 to 7 still count toward your rate limit, and so does GET /v1/me.
Methods and parameters
Section titled “Methods and parameters”- Version 1 only has
GETendpoints. Any other method, includingOPTIONSandHEAD, returns404 not_foundonce the key is valid. A request without a key gets401first, and that is what a browser preflight receives. GET /v1/meandGET /v1/referrers/{id}take no query parameters. Adding one returns400 invalid_request.- Unknown parameters, repeated parameters (
?email=a&email=b) and array or object syntax (?email[]=a) return400 invalid_request. - Query values are read like form data, so a
+means a space. Encode a+in an email as%2B.
Rate limits
Section titled “Rate limits”The limit applies per store and is shared by every key of the store. Today every eligible plan gets 100 requests per minute. Read the current value from limits.requests_per_minute in GET /v1/me, or from the headers below.
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
Requests allowed in a 60-second window |
X-RateLimit-Remaining |
Requests left in the current window |
X-RateLimit-Reset |
Seconds until the current window ends |
Retry-After |
On 429 and 503: seconds to wait before you retry |
Responses that pass the key and store checks carry the X-RateLimit-* headers. Errors returned before the rate limit is checked do not: 401, 403 plan_not_eligible, 403 shop_inactive, the IP block 429, and a 503 while the key is checked.
When you get 429, wait for Retry-After and retry. Spread large syncs out instead of sending pages as fast as possible.
Separately, after 30 requests with a missing, malformed or invalid key from one IP within 60 seconds, every request from that IP gets 429 until that 60-second window ends. A loop that retries a revoked key can lock out your other integrations on the same server.
Retry or not
Section titled “Retry or not”- Retry with backoff:
429,500,503. HonorRetry-Afterwhen it is present. - Don’t retry unchanged:
400,401,403,404. The same request gets the same answer.
Request ids
Section titled “Request ids”Every response carries an X-Request-Id header, and every error repeats it as request_id. Quote it when you contact support. To tie Bloop’s answer to your own logs, send your own X-Request-Id: 8 to 64 letters, digits, _ or -. A value in another format is replaced by one Bloop generates.
Common mistakes to avoid
Section titled “Common mistakes to avoid”- Parsing
message. Branch oncode. Messages change. - Retrying a
400or401in a loop. It never succeeds, and repeated401responses get your IP blocked. - Giving each integration its own budget in your head. All keys of a store share one limit.
- Ignoring
Retry-After. Retrying sooner just earns another429.
Next steps
Section titled “Next steps”- See which errors each endpoint returns in Understanding the API endpoints.
- Build a sync that respects the limit in How to make your first API call.
- Handle webhook delivery failures in How to receive webhooks.

