> For the complete documentation index, see [llms.txt](https://docs.insurgrid.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.insurgrid.com/policy-extraction-api.md).

# Policy Extraction API

Send one policy PDF, get back its contents as structured data: policy details, coverages, drivers, vehicles, premiums and more.

A policy PDF holds everything you need — limits, deductibles, drivers, vehicles, premiums — but not in a form your systems can use. This API does the reading for you.

Upload a policy and say what kind it is. InsurGrid reads the document and returns its contents as structured JSON, ready to drop into your CRM, quoting tool or review workflow.

It works on personal and commercial lines, from auto and homeowners through to workers' compensation, cyber and commercial packages.

**You'll need:** an agent API key, and the policy document as a PDF.

***

## How it works

Reading a policy takes longer than a web request should, so the work happens in the background:

1. **Submit** the PDF in one request.
2. **Get an id back** straight away, while processing continues.
3. **Poll** with that id every few seconds.
4. **Stop** when the status is `completed` or `error`.

A typical extraction finishes in under a minute.

## Before you start

Every request carries your API key as a bearer token:

```
Authorization: Bearer YOUR_API_KEY
```

This is the same key the [Policy Comparison API](/policy-comparison-api.md) uses — create and revoke it in the agent dashboard. A key belongs to the agent who made it, and only that agent can read the extractions it creates.

Keys are server-side credentials. Keep them out of browser code, mobile apps, public repositories and logs.

| Environment | Base URL                                |
| ----------- | --------------------------------------- |
| Staging     | `https://phoenix-staging.insurgrid.com` |
| Production  | `https://phoenix.insurgrid.com`         |

Everything below lives under `/api/v1`. Only the host changes between environments — paths, fields and errors are identical, and a key works in one environment only. See [Environments](/guides/environments.md).

***

## Quick start

**Submit an extraction.** The PDF goes in the same request — there's no separate upload step.

```bash
curl -X POST 'https://phoenix-staging.insurgrid.com/api/v1/policy-extractions' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -F 'policy_type=auto_policy' \
  -F 'file=@policy.pdf;type=application/pdf'
```

You get a `202 Accepted` and an id:

```json
{
  "id": "09342f6c-3c68-4747-90d9-2090599748b0",
  "status": "processing",
  "createdAt": "2026-10-14T11:53:39Z"
}
```

**Then poll for the result**, every 3–5 seconds:

```bash
curl 'https://phoenix-staging.insurgrid.com/api/v1/policy-extractions/09342f6c-3c68-4747-90d9-2090599748b0' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

While it's working you'll see `"status": "processing"`. When it's done, the same response carries a `result`.

***

## Submitting an extraction

`POST /api/v1/policy-extractions`, as `multipart/form-data`.

| Field         | Required | What it is                                         |
| ------------- | -------- | -------------------------------------------------- |
| `policy_type` | Yes      | Which kind of policy this is. See the table below. |
| `file`        | Yes      | The policy PDF. Exactly one file per request.      |

No other fields are accepted. To extract several policies, send one request per PDF.

### Policy types

| Personal                   | Commercial                      |
| -------------------------- | ------------------------------- |
| `auto_policy`              | `commercial_auto_policy`        |
| `home_policy`              | `commercial_home_policy`        |
| `condo_policy`             | `commercial_bop`                |
| `renters_policy`           | `commercial_general_liability`  |
| `landlord_policy`          | `commercial_worker_comp_policy` |
| `umbrella_policy`          | `commercial_umbrella`           |
| `flood_policy`             | `commercial_flood`              |
| `cyber_policy`             | `commercial_cyber`              |
| `marine_policy`            | `commercial_marine`             |
| `power_sports_policy`      | `commercial_directors_officers` |
| `pet_policy`               | `commercial_package`            |
| `general_liability_policy` | `commercial_other`              |
| `other_policy`             |                                 |

Anything else returns `422 unsupported_policy_type`. Pick the type that matches the document — it decides which fields the extraction looks for.

### Document requirements

PDFs only — sent with a `.pdf` extension and the `application/pdf` content type. Password-protected, corrupted or unreadable files may fail during processing.

| Limit            | Value |
| ---------------- | ----- |
| PDFs per request | 1     |
| File size        | 75 MB |
| Pages per PDF    | 400   |

Large scanned PDFs are compressed automatically before they're read, so you don't need to shrink them yourself.

***

## Reading the result

`GET /api/v1/policy-extractions/{id}`, using a key belonging to the agent that created it.

| Status       | Done? | What it means                             |
| ------------ | ----- | ----------------------------------------- |
| `processing` | No    | Still reading the document. Keep polling. |
| `completed`  | Yes   | The extracted data is in `result`.        |
| `error`      | Yes   | Processing failed. Stop polling.          |

A completed response, trimmed:

```json
{
  "id": "09342f6c-3c68-4747-90d9-2090599748b0",
  "status": "completed",
  "policyType": "auto_policy",
  "filename": "policy.pdf",
  "createdAt": "2026-10-14T11:53:39Z",
  "result": {
    "document_id": "09342f6c-3c68-4747-90d9-2090599748b0",
    "policy_type": "auto_policy",
    "structured_data": {
      "policy": {
        "provider": "Sample Mutual Insurance Company",
        "policy_number": "TEST-AUTO-0000001",
        "policy_term": "6 Months"
      },
      "policyholder": { "name": "Testy McTestface", "address": {} },
      "drivers": [
        { "name": "Testy McTestface", "date_of_birth": "1980-01-01", "relationship_to_policyholder": "Self" }
      ],
      "vehicles": [
        {
          "year": 2020,
          "make": "Toyota",
          "model": "Camry",
          "coverages": [
            { "type": "Bodily Injury Liability", "limits": "$100,000 / $300,000", "premium": 412.0, "deductible": 0 }
          ]
        }
      ],
      "discounts": [],
      "surcharges": [],
      "billing_information": {},
      "insurer_information": {}
    }
  }
}
```

The extracted data is in `result.structured_data`. Its sections follow the policy type — an auto policy has `drivers` and `vehicles`, a homeowners policy has the dwelling and its coverages, and commercial policies carry their own sections. Common ones include:

| Section                      | What it gives you                                                  |
| ---------------------------- | ------------------------------------------------------------------ |
| `policy`                     | Carrier, policy number, term, dates and premium totals.            |
| `policyholder`               | Named insured, address and contact details.                        |
| `drivers`, `vehicles`        | Auto only: who is covered, what is covered, per-vehicle coverages. |
| `discounts`, `surcharges`    | What's been applied to the premium.                                |
| `endorsements`, `exclusions` | Changes to and limits of the standard coverage.                    |
| `billing_information`        | Payment status and due dates.                                      |
| `insurer_information`        | The carrier and the agent on the policy.                           |

What appears depends on the policy type and on what the document actually contains — fields can be absent, empty or `null`.

> **Check the result against the policy document.** The data is generated from the PDF you supplied. Confirm limits, exclusions, deductibles and premiums against the original before relying on them.

### When it fails

```json
{
  "id": "09342f6c-3c68-4747-90d9-2090599748b0",
  "status": "error",
  "policyType": "auto_policy",
  "filename": "policy.pdf",
  "createdAt": "2026-10-14T11:53:39Z",
  "error": {
    "code": "extraction_failed",
    "message": "The extraction could not be completed."
  }
}
```

No further polling is needed. You can submit the same PDF again.

### If it never finishes

An extraction should end in `completed` or `error`. If one is still `processing` after **10 minutes** (**20 minutes** for `commercial_package`), treat it as failed and submit the PDF again.

***

## Errors

Errors always arrive in the same shape:

```json
{
  "error": {
    "code": "invalid_request",
    "message": "Required multipart fields are missing or malformed."
  }
}
```

When the failure relates to an extraction that was already created, the body also carries `error.extractionId`.

| Status | Code                           | What went wrong                                                                         |
| ------ | ------------------------------ | --------------------------------------------------------------------------------------- |
| 400    | `invalid_request`              | A field is missing, duplicated, unexpected or malformed — including more than one file. |
| 401    | `invalid_api_key`              | The key is missing, invalid, revoked, or from the other environment.                    |
| 404    | `not_found`                    | No such extraction on this key's agent.                                                 |
| 413    | `file_too_large`               | Over the size or page limit.                                                            |
| 415    | `pdf_required`                 | Something that isn't a readable PDF.                                                    |
| 422    | `unsupported_policy_type`      | `policy_type` isn't one of the values above.                                            |
| 429    | `rate_limited`                 | Too many requests. Wait for `Retry-After` seconds.                                      |
| 500    | `extraction_submission_failed` | The extraction couldn't be submitted. Try again.                                        |
| 503    | `service_unavailable`          | Temporarily unavailable. Try again.                                                     |

## Rate limits

**60 requests per minute per agency**, counting both submissions and reads. Going over returns `429` with a `Retry-After` header telling you how many seconds to wait.

Extractions aren't metered — there are no credits to run out of, and this limit is the only throttle. It's separate from the Policy Comparison API's: extraction calls don't use up your comparison allowance, and the other way round.

## Keeping it safe

Policy documents contain personal information, so:

* call the API from your server, never the browser;
* store keys in a secrets manager or encrypted environment variable;
* use a separate key per agent or integration, and revoke immediately if one may have leaked;
* avoid logging uploaded documents or whole API responses.

## Related

* [Policy Comparison API](/policy-comparison-api.md) — compare a current and a proposed policy.
* [Environments](/guides/environments.md) — staging and production addresses.
* [Authentication & tokens](/guides/authentication.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.insurgrid.com/policy-extraction-api.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
