Skip to main content
The Finta API enforces two general request limits, signup email allowances, and a capacity limit for report exports. All can surface as 429 Too Many Requests. Branch on error.code and honor Retry-After for request or signup email limits.

Per-minute burst limit

Authenticated requests are limited to 6,000 requests per 60 seconds (100 per second) per API key. Unauthenticated requests (missing or invalid key) are limited to 120 requests per 60 seconds per IP address. The window is fixed, not rolling. Each 60-second wall-clock interval is its own counter; all requests within that interval share it, and the counter resets to 0 in a single step at the next boundary. Available budget jumps from 0 back to the full bucket size (6000 for authenticated API keys, 120 for unauthenticated IP buckets) instantaneously at the boundary, rather than aging out one slot at a time. Trust X-RateLimit-Reset for the exact reset timestamp; do not assume requests age out individually.

Headers

Successful responses and burst-limit 429s carry three headers so you can monitor consumption: Do not require these headers on error responses. Rejections that happen before the burst counter is consulted, including authentication and authorization failures and monthly-limit rejections, can omit all three. Code that reads them unconditionally will break on those responses. This is how the headers have always behaved; it is not a new quota policy. A 429 in this case adds one more header:

Example error body

Retry guidance

This 429 is retryable. Recommended pattern:
  1. Honor Retry-After as the minimum wait. Do not retry sooner.
  2. For retries beyond the first, apply exponential backoff with jitter (e.g. base = Retry-After, double on each attempt, add random jitter in [0, 1s], cap at a few minutes).
  3. Cap the total number of attempts (e.g. 3 to 5) and surface the failure if exhausted, rather than retrying indefinitely.
  4. Treat X-RateLimit-Remaining as a leading indicator: if it is near zero on a successful response, slow down voluntarily before the next request to avoid the 429 entirely.

Signup email allowances

Agent signup is always available through REST and signed-out MCP, subject to the request and email limits below. POST /signups always uses 120 requests per minute per IP, even when a credential is supplied. Anonymous MCP discovery/signup uses a separate fixed window of 6,000 requests per minute per IP; that initial shared-provider-IP budget is pending confirmation. These are request budgets, not email budgets. REST and MCP signup share verification allowances with web signup: For signup verification emails only, requests from Anthropic’s or OpenAI’s published connector egress ranges use one shared bucket per provider instead of the ordinary IP bucket. Each provider has its own bucket. Recipient limits are unchanged. Other auth email purposes, including password resets and user invitations, keep the ordinary per-IP limits even on provider IPs. The ranges are pinned in code from the published lists fetched on October 29, 2026; they are not fetched at runtime: New, pending and existing accounts consume the same allowance. Changing User-Agent, rotating recipient emails, or switching between one provider’s matching IPs does not bypass its shared signup bucket. A 202, absent email or 429 does not reveal account existence. Retrying can renew a pending link and invalidate the previous one. The X-RateLimit headers accompany completed REST signup responses and request-limit rejections; early parameter exceptions may omit them. Use Retry-After for either signup 429 rather than inferring a wait from the request budget. For example, an email allowance rejection can return:
Wait at least that many seconds, use bounded backoff for subsequent retries, and avoid retrying indefinitely. Signup consumes no monthly API or MCP allowance and creates no API request log. Failures creating an account or sending email after admission do not refund the email allowance or change the public 202.

Monthly per-company limit

The company’s plan determines its monthly API allowance, independent of the per-minute burst limit: To read the plans and their standard limits at runtime instead of copying this table, call GET /plans. It returns null for every Enterprise limit, which is set by contract. See Plans and limits. For this company’s own limit and how many calls it has used this month, call GET /usage and read api_calls. See Usage. The limit is per company, not per API key. All keys for a company share the same pool. Enterprise is enabled internally by Finta for contract-managed companies. It bypasses commercial monthly API/MCP quotas and ordinary subscription lockouts, including when Stripe is absent or canceled. Requests are still counted. Valid credentials, company/user permissions, company closure, the independent linked billing-invoice gate, request validation, payload bounds and per-minute abuse limits still apply. Disabling Enterprise restores the ordinary plan gates without changing Stripe billing. Finta support can set a temporary limit override for a company for the current month. It applies to every user and every API key in the company, and usage is still the company’s total; there is no separate allowance for one user or one key. The override changes both the cap that applies and the number quoted in the error message, and it expires at the end of the month it was set in. The error.code and the do-not-retry rule are the same either way, so read the allowance from the message, or from api_calls.limit in GET /usage, where api_calls.custom_limit is true while an override applies, rather than assuming the plan’s number from the table above. Counts reset at midnight Pacific Time (US & Canada) on the 1st of each month. This is the Finta application’s configured time zone; the cap is computed in that zone, not in UTC. Consumers in other time zones should convert that local-midnight Pacific boundary to their own clock. The absolute UTC offset shifts by one hour twice a year for daylight saving (PST is UTC-8, PDT is UTC-7), so consumers needing exact second-level precision near month boundaries should account for the active offset on the date in question. What counts: after the company is onboarded, authenticated requests that reach the monthly meter count regardless of HTTP status. A 400 from bad parameters counts the same as a 200. Unauthenticated requests (401) do not count, and blocked requests (after hitting the monthly limit) are also not counted, so hitting the cap does not extend the cap-exceeded period. What doesn’t count: GET /onboarding and PATCH /onboarding are always free, even after onboarding or the monthly cap. GET /plans and GET /usage are exempt from the monthly limit. They are never counted and never refused with monthly_limit_exceeded, even after the company has used its whole allowance, so a caller at its limit can still see what upgrading offers and read api_calls.stopped. The per-minute burst limit still applies to them, with the usual Retry-After and X-RateLimit-* headers. GET /billing, which reads what the company pays, is not exempt and counts like any other request.

Onboarding calls

Until the company is onboarded, no API or MCP call counts toward the monthly allowance, and reaching that allowance does not block onboarding. Calls are still logged with counted: false. The onboarding gate limits which endpoints can answer; quota exemption does not bypass that gate or the usual permissions and billing checks. GET /onboarding and PATCH /onboarding, and MCP get_onboarding and update_onboarding, never count toward their respective monthly allowances, after setup too. Their per-minute burst and existing security limits still apply. This lets a company on Formation complete setup without spending its 10-call monthly allowance. The independent billing-invoice gate runs before monthly allowance checks, including on GET /plans and GET /usage. Its 403 invoice_overdue and 503 invoice_status_unavailable refusals are not counted. Pay overdue linked invoices rather than retrying continuously; for a temporary verification failure, honor Retry-After: 60 and use bounded backoff. Existing security burst limits are unchanged. The 1099 filing endpoints use the same allowance. List returns individual records and full yearly summaries in one call; no additional summary request is needed. Retrieving a filing again to refresh an expired document link consumes another API call.

Example error body

With a temporary override, the message quotes that limit instead, for example Monthly API limit reached. Your temporary limit allows 1000 calls for this company this month. Resets on November 1, 2026. Retry-After is not sent in this case. The X-RateLimit-* burst-window headers may be present or absent here, and either way they describe the burst window rather than the monthly cap. Nothing in the response reports remaining monthly quota.

Retry guidance

This 429 is not retryable in the short term. The only meaningful remediation is to upgrade the plan or wait until the 1st of the next month. Recommended pattern:
  1. Branch on error.code first. rate_limit_exceeded is the burst case (retryable, see above). monthly_limit_exceeded is this case.
  2. If the code is monthly_limit_exceeded, stop retrying. Surface the error to your user with the reset date from the message body, or page the operator to upgrade the plan. GET /plans still answers at the limit if you want to show what each plan allows, and GET /usage still answers too: once its api_calls.stopped is false, for example after an upgrade, calls can resume.
  3. SDKs should branch on error.code directly. Missing Retry-After does not universally mean never retry: export capacity can free up when an existing job finishes.

Report export capacity

A company may have at most five unfinished API exports, across all submitters. New requests above that cap return 429 report_export_limit_exceeded without creating or enqueueing a job. Existing-key replays and retrieval remain available, subject to the ordinary API request limits. A terminal success or failure releases its slot. Enterprise does not bypass this export-capacity guard. This response has no Retry-After, because generation finish time is unknown:
Poll existing exports with backoff, then retry the rejected submission after one finishes. Submission, replay, polling and retrieval to refresh a download link each consume an API call. The range cap of 120 calendar months is separate: an oversized export returns 400 invalid_date_range, not 429. PDF exports also support at most 13 displayed periods; use coarser grouping, CSV or XLSX for more columns. See Report Exports.

Sheets selection limits

A selected sheet read accepts up to 100 bounded ranges and 10,000 selected cells total; overlapping selections each count toward the total. List/search pagination accepts 1–500 results, default 100. Exceeding a selection limit is 400 invalid_request, not a rate-limit 429: reduce the selection before retrying. Calculation is synchronous and bounded. An HTTP 200 with calculation.status: unavailable omits value matrices rather than inventing results; retry only the needed ranges with bounded backoff. There is no calculation job to poll. Shared request-rate and monthly limits still apply.

Sheets mutation budget

Custom sheet creation and editing accept at most 500 expanded operations per request, shared across all worksheets, ranges and charts. Each non-null cell write and each formatted cell counts separately. Tab setup, chart operations, renames, structural edits, automatic grid expansion and any writes needed to preserve formulas also count. Clears count even for blank cells: contents or formats costs one per cell; all costs two. Copy/fill can emit both value and format edits. Format, clear and copy/fill selection rectangles are bounded to 500 cells. Null entries skip their cell writes; sparse rectangles can exceed 500 cells in area within company grid limits. Template seeding is outside this operation budget. Splitting ranges within one request does not reset the budget. Exceeding it returns 400 invalid_request, not a usage-quota 429, and saves nothing. Reduce the batch before retrying; for larger custom documents, create a smaller initial document and populate it with sequential PATCH calls. Each PATCH uses the previous successful response’s revision. Splitting into separate calls is not atomic across those calls: earlier successful calls stay committed if a later one fails. The normal API/MCP call meters still apply independently to each call. A document is limited to 100 tabs, 50 charts per tab and a 1 MiB normalized snapshot; company grid limits normally allow 500 rows and 52 columns. Creation and PATCH’s optional return_ranges use the same 100-selection/10,000-cell bound as reads. A saved mutation can return a calculation error or unavailable status; retain its ID/revision and reread selected cells rather than repeat the mutation solely to recalculate.

Best practices

  • Branch on error.code, not the status code alone. Burst, monthly and export-capacity errors all use 429 but require different recovery. A generic retry loop can waste calls on monthly exhaustion or export capacity.
  • Check headers proactively. Monitor X-RateLimit-Remaining and slow down before you hit the burst limit.
  • Use larger page sizes. When paginating, set limit up to 500 to pull more data per request and reduce total calls.
  • Cache responses. If you are repeatedly pulling the same data, cache it on your end to conserve your monthly quota.
  • Check connection requests sparingly. Polling GET /integrations/{key}/connect every 10 seconds for a link nobody opens costs about 90 calls over an OAuth link’s 15 minutes, or 180 over a Plaid Hosted Link’s 30 minutes. Where you can talk to the user, ask them to say when they have approved, then check once. See Polling and API usage.
  • Batch edits, then back off when polling. A bulk update of up to 5,000 transactions costs one call to submit, not one per transaction. Each identical replay, each poll of GET /jobs/{id}, and each page of its errors is another call, so back off between polls and stop at success or failed. See Polling and retention.
  • Group totals in one call. To rank merchants or chart a category month by month, use GET /aggregations/totals instead of calling GET /aggregations/total once per merchant or per month. One grouped request answers what often took 100 or more per-merchant calls.
  • Upload in full chunks. A transaction upload takes up to 500 rows per call, and a clean file of 500 rows or fewer takes one call with commit: true. Every call counts, including a status check that sends only upload_id, so wait between checks on an importing upload.
  • Sync transactions incrementally. If you keep a local copy of transactions, use updated_after to pull only what changed since your last run instead of re-reading every page to discover that a few rows changed.