Skip to main content
Signup Risk API separates four concepts that developers commonly confuse. Each behaves differently and fails differently:
Plan limits can change independently of API semantics. See Pricing for current allowances.

Warnings are not failures

A warning in metadata.warnings never means the request failed. An analysis can return a complete decision while reporting that some optional evidence was unavailable or a quota threshold is near. Warnings tell you what to know about the response — not what to redo. Each warning carries a code, a human-readable message, and — for plan-related warnings — upgrade context (upgrade_required, required_plan, recommended_plan, and where applicable an upgrade_url).

Public warning codes

UNKNOWN_INDUSTRY fires for any unrecognized value on any plan. IP_INTELLIGENCE_NOT_AVAILABLE fires only when you explicitly supply an ip in the request body; when no IP is supplied, the omission is silent. Warnings about unavailable evidence affect the decision’s quality context: expect decision.assessment to widen (evidence_coverage down, uncertainty up) when evidence was omitted.

Plan feature gates

Features and request context are gated by plan. The failure mode differs per feature — this table is the authoritative map of what happens when a plan does not include something: FEATURE_NOT_AVAILABLE responses include remediation metadata: upgrade_required, required_plan, recommended_plan, and an upgrade action linking to your billing page.

Monthly usage quota

What is counted: one unit per analyzed email address — one for a single analysis, one per row in a batch request, one per processed row in a bulk job. Window: usage resets monthly. The window is anchored to your account (typically your subscription start date) — metadata.plan.reset_at always reports the exact reset time, and metadata.plan.billing_period shows the current window. Grace buffer: requests keep succeeding for a grace allowance beyond the monthly limit (10% of the plan quota), accompanied by an IN_GRACE_BUFFER warning. At the hard limit, further requests fail. Exhaustion: past the hard limit, analysis requests fail with 429 MONTHLY_QUOTA_EXCEEDED. The error body includes a details snapshot (plan, cap, grace, hard_stop, current_usage, requested_units, reset_date) and a retry_after_seconds value reflecting the reset time, mirrored in the Retry-After header. Retrying immediately does not help — upgrade the plan or wait for the reset. Refunds: if the API fails internally while processing an analysis, the units for that request are returned to your quota. Requests that were analyzed are not refunded.

Batch accounting

POST /v1/analyze-email/batch analyzes up to 50 addresses per request.
  • Each submitted row consumes one unit, committed when the batch is accepted.
  • Rows that fail per-row validation (for example, a malformed address) still consume their unit — the analyzer was invoked for them; the failure is reported in that row’s error field while the overall request returns 200.
  • An oversized batch (more than 50 rows) is rejected with 400 LIMIT_EXCEEDED before units are committed — no quota is consumed.
  • If the connection drops mid-processing, rows that were never attempted are refunded.

Bulk accounting

Asynchronous bulk jobs (POST /v1/bulk/jobs) are accounted differently:
  • Submitting a job consumes no units.
  • While the job processes, units are reserved and then consumed per row actually analyzed.
  • Rows that are never processed — because the job stops, fails, or hits the quota — are released back to your allowance. Net consumption equals rows analyzed.
  • If the monthly quota is exhausted mid-job, the job stops with status quota_exceeded; already-processed rows are available in the results.
Batch analysis (synchronous, up to 50 rows) and bulk jobs (asynchronous, large lists) are separate mechanisms — do not confuse their quota behavior.

Explanation allowance

Explanations (options.include_explanation) draw on a separate monthly allowance included in each plan; each analyzed row with an explanation requested consumes one explanation unit. When the allowance is exhausted, the analysis itself still succeeds — this is not a 429:
  • explanation.explanation_available is false and explanation_omitted_reason is "limit_exceeded"
  • the X-Explanation-Omitted: true response header is set
  • an EXPLANATION_LIMIT_EXCEEDED warning appears in metadata.warnings
The request unit is still consumed; explanation units are not.

Rate limiting

Rate limits are short-window throttles, independent of the monthly quota. Per-minute plan limit: each plan defines a requests-per-minute allowance, counted in analyzed units — a 50-row batch counts as 50 units against the minute. Exceeding it returns 429 RATE_LIMIT_EXCEEDED with a details snapshot (rate_limit, window, retry_after, requested_units); the Retry-After header and error.retry_after_seconds both carry the seconds remaining in the current minute. Anti-abuse limits: all endpoints are additionally protected by per-IP safeguards. These thresholds are intentionally not published, and their responses do not include Retry-After — if you receive a RATE_LIMIT_EXCEEDED without retry metadata, reduce your request frequency. Duplicate protection: an identical request (same account, endpoint, email, and options) still in flight returns 429 DUPLICATE_REQUEST, which is not retryable — wait for the original request to finish. Rate limiting (slow down, retry shortly) and quota exhaustion (upgrade or wait for the monthly reset) are different conditions with different remedies, even though both use HTTP 429.

Quota and rate-limit headers

Successful analysis and batch responses carry the full snapshot as headers: Error responses carry X-Request-ID, and Retry-After where applicable, instead of the quota headers.

Retry guidance

Next steps

Error Reference

Every public error code, its status, meaning, and whether to retry it.