> ## Documentation Index
> Fetch the complete documentation index at: https://docs.signuprisk.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Warnings & Quotas

> Understand non-fatal warnings, plan gates, usage quotas, rate limits, and retry behavior.

Signup Risk API separates four concepts that developers commonly confuse. Each behaves differently and fails differently:

| Concept                   | What it governs                                                | When exceeded                                                   |
| ------------------------- | -------------------------------------------------------------- | --------------------------------------------------------------- |
| **Plan feature gate**     | Whether a feature or request context is available on your plan | Hard `403` error, or soft omission with a warning (per feature) |
| **Monthly usage quota**   | How many analyzed addresses your plan allows per month         | `429` `MONTHLY_QUOTA_EXCEEDED`                                  |
| **Request rate limit**    | How many analyzed units you may send per minute                | `429` `RATE_LIMIT_EXCEEDED`                                     |
| **Explanation allowance** | How many explanations per month your plan includes             | Analysis still succeeds; explanation omitted with a warning     |

<Note>
  Plan limits can change independently of API semantics. See
  [Pricing](https://www.signuprisk.com/pricing) for current allowances.
</Note>

## 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

| Code                            | What happened                                                                 | Analysis completed?                     | What to do                                                                |
| ------------------------------- | ----------------------------------------------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------- |
| `APPROACHING_QUOTA`             | At least 90% of the monthly request allowance has been used                   | Yes                                     | Monitor `metadata.usage`; upgrade before the reset if the trend continues |
| `IN_GRACE_BUFFER`               | Usage has passed the monthly allowance and is inside the grace buffer         | Yes                                     | Upgrade or wait for the reset — requests stop at the hard limit           |
| `EXPLANATION_LIMIT_EXCEEDED`    | The explanation allowance is exhausted                                        | Yes — explanation omitted               | The decision and checks are unaffected; upgrade or wait for the reset     |
| `UNKNOWN_INDUSTRY`              | The `industry` value is not a recognized profile                              | Yes — scored with the `default` profile | Correct the `industry` value                                              |
| `INDUSTRY_NOT_AVAILABLE`        | The industry profile is not available on the current plan                     | Yes — scored with the `default` profile | Upgrade, or omit `industry` to avoid the warning                          |
| `IP_INTELLIGENCE_NOT_AVAILABLE` | An `ip` was supplied but IP intelligence is not available on the current plan | Yes — IP evidence omitted               | Upgrade, or omit `ip` to avoid the warning                                |

`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                                      | Required plan      | When not included                                                                                                                                                        |
| -------------------------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| IP context (`ip` field)                      | Growth and above   | **Soft omission** — request succeeds; `ip_reputation` reports `available: false` and contributes nothing; `IP_INTELLIGENCE_NOT_AVAILABLE` warning when `ip` was supplied |
| Industry profiles (`industry` field)         | Scale and above    | **Soft fallback** — request succeeds, scored with the `default` profile; `INDUSTRY_NOT_AVAILABLE` warning                                                                |
| Bulk jobs (`/v1/bulk/*`)                     | Scale and above    | **Hard error** — `403` `FEATURE_NOT_AVAILABLE`                                                                                                                           |
| Webhooks (`/v1/webhooks/*`)                  | Scale and above    | **Hard error** — `403` `FEATURE_NOT_AVAILABLE`                                                                                                                           |
| Explanations (`options.include_explanation`) | Allowance per plan | **Soft omission** — request succeeds without the explanation (see below)                                                                                                 |

`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:

| Header                                                              | Meaning                                                          |
| ------------------------------------------------------------------- | ---------------------------------------------------------------- |
| `X-Quota-Limit`                                                     | Monthly unit allowance for the plan (`-1` when unlimited)        |
| `X-Quota-Used`                                                      | Units used in the current window                                 |
| `X-Quota-Remaining`                                                 | Units remaining against the hard limit                           |
| `X-Quota-Reset`                                                     | RFC 3339 reset timestamp                                         |
| `X-Quota-Grace` / `X-Quota-HardStop`                                | Grace allowance and the hard limit it produces                   |
| `X-RateLimit-Limit` / `X-RateLimit-Remaining` / `X-RateLimit-Reset` | Per-minute allowance, remaining units, and reset (epoch seconds) |
| `X-Explanation-Omitted`                                             | `true` when the explanation allowance is exhausted               |
| `X-Request-ID`                                                      | Correlation ID for the request                                   |

Error responses carry `X-Request-ID`, and `Retry-After` where applicable, instead of the quota headers.

## Retry guidance

| Condition                                          | Retry?               | Guidance                                                           |
| -------------------------------------------------- | -------------------- | ------------------------------------------------------------------ |
| Temporary rate limit (`RATE_LIMIT_EXCEEDED`)       | Yes                  | Wait for `retry_after_seconds` / `Retry-After`, then retry         |
| Monthly quota exhausted (`MONTHLY_QUOTA_EXCEEDED`) | Not immediately      | Upgrade the plan or wait for `reset_at`                            |
| Duplicate request in flight (`DUPLICATE_REQUEST`)  | No                   | Wait for the original request to complete                          |
| Invalid request (4xx validation)                   | No                   | Correct the request and resend                                     |
| Warnings about unavailable optional evidence       | Usually not required | The decision is valid; use the returned decision and assessment    |
| Explanation allowance exhausted                    | Not required         | The analysis succeeded; the explanation returns after the reset    |
| Unexpected server error (500 `INTERNAL_ERROR`)     | Conservative retry   | Retry with backoff; keep the `X-Request-ID` if you contact support |

## Next steps

<Card title="Error Reference" icon="circle-alert" href="/errors">
  Every public error code, its status, meaning, and whether to retry it.
</Card>
