links.et

Rate limits

Two different ceilings, the request rate and the plan's verification cap, plus the screenshot read allowance.

Two separate ceilings apply to every call, and they fail with different codes:

  • Request rate, per key, per minute. Protects the service from a hot loop.
  • Verification cap, per plan, per window. Counts only receipts we have to fetch from a bank. This is the one tied to what you pay.

A third, the screenshot read allowance, applies to POST /api/verify-image only.

Request rate

Every key has a per-minute budget, 60 requests/minute by default. Each request increments a bucket keyed on the current minute. Go over it and the rest of that minute returns 429 rate_limited with a Retry-After header holding the seconds until the next minute boundary.

The limit is stored per key, so it can be raised on one key without touching the others. Ask if 60 is not enough.

Verification cap

Only uncached verifications count. A receipt we have already fetched is served from cache, costs us nothing, and never counts however many times you ask for it. That is why these numbers can be stated plainly rather than hidden behind a fair-use asterisk.

PlanUncached receiptsWindowOver the cap
Free200calendar monthHard stop
Shop2,000dayHard stop
Developer30,000calendar month0.05 ETB each, opt-in
Scale150,000calendar month0.03 ETB each, opt-in
EnterprisePer contractcalendar month0.03 ETB each, opt-in

Overage is off by default on every plan that offers it. Until you turn it on from the dashboard, passing the cap stops the call rather than billing you for it. Free and Shop have no overage path at all.

Passing the cap returns 429 quota_exceeded, not rate_limited. The body carries used and cap, and Retry-After holds the seconds until the window rolls over:

{
  "ok": false,
  "error": {
    "code": "quota_exceeded",
    "message": "Plan 'Free' allows 200 new (uncached) verifications per month. Cache hits are always free. Upgrade or enable overage to continue.",
    "used": 200,
    "cap": 200
  }
}

Waiting out a rate_limited takes seconds. Waiting out a quota_exceeded can take until the end of the month, so treat them differently in your retry code.

Screenshot reads

POST /api/verify-image runs the image through an AI extractor before it can verify anything, and that call costs us money whether or not the receipt turns out to be cached. It gets its own allowance, counted per calendar month on every plan, whatever window that plan's verification cap uses:

PlanScreenshot reads / month
Free200
Shop1,000
Developer2,000
Scale6,000
EnterprisePer contract

There is no overage path here: reaching the allowance returns 429 image_cap_reached until the month rolls over. Reading a screenshot is independent of verifying the receipt it contains, so a read that resolves to an already-cached receipt still spends one read and no verification.

Priority

When a bank is saturated and calls have to queue, the plan decides who is served first: Scale and Enterprise, then Developer, then Shop and Free. Priority changes your position in the queue, never your cap.

Anonymous demo

The landing page demo takes no API key and is metered per IP, not per account: 10 verifications and 3 screenshot reads per hour. It exists so you can try the thing before signing up. Anything built against it will stop working.

Raising a limit

Email robelmezemir@gmail.com with your key prefix and what you are building. Request rate and Enterprise caps are set per key; plan caps move by changing plan.

On this page