PW ProWriters Services
ProWriters

ProWriters Services

API tools and integrations built for insurance workflows

Services

Production-ready APIs you can integrate today.

📄

PDF Extractor API

Extract structured data from insurance application PDFs using AI. Upload a PDF, get back every field, section, and value — as JSON or CSV.

Live
🔧

More coming soon

Additional API services for insurance workflows are in development. Check back for updates.

Coming Soon

PDF Extractor API

https://api.prowritersservices.com/api/v1

Authentication

All /api/v1 endpoints require an API key in the header:

X-API-Key: pdfx_your_key_here

Create keys using the Key Management endpoints below.

Rate Limiting

Each API key is limited to 60 requests per 60 seconds.

Every response includes these headers:

HeaderDescription
X-RateLimit-LimitMax requests per window (60)
X-RateLimit-RemainingRequests remaining in this window
X-RateLimit-ResetUnix timestamp when window resets

When exceeded, the API returns 429 Too Many Requests.

Concurrent Extraction Limit

Each API key can have at most 5 active extractions (status queued or processing) at once. If you hit this limit, the API returns 429 with code concurrent_limit.exceeded. Wait for in-progress jobs to finish before submitting more.

Admin Endpoints

Key management endpoints (POST /keys, GET /keys, DELETE /keys/{id}) have a stricter limit: 5 requests per 60 seconds per key.

Response Format

Every response uses a consistent envelope:

Success:

{
  "status": "success",
  "data": { ... },
  "meta": {
    "request_id": "abc123",
    "timestamp": "2026-02-14T12:00:00+00:00",
    "version": "1.0"
  }
}

Error:

{
  "status": "error",
  "error": {
    "code": "auth.invalid_key",
    "message": "Invalid API key."
  },
  "meta": {
    "request_id": "abc123",
    "timestamp": "2026-02-14T12:00:00+00:00",
    "version": "1.0"
  }
}

Endpoints

Upload & Extract PDF

POST /api/v1/extract

Upload a PDF for data extraction. Returns immediately with a job ID.

Request

Content-Type: multipart/form-data

FieldTypeDescription
fileFilePDF file (max 32 MB, max 100 pages)

Response — 202 Accepted

{
  "status": "success",
  "data": {
    "id": 1,
    "filename": "application.pdf",
    "job_status": "queued",
    "message": "Extraction job submitted. Poll GET /api/v1/extractions/{id} for results."
  }
}

Errors

StatusCodeCause
400validation.errorNot a PDF, empty, or too large
401auth.missing_keyNo X-API-Key header
401auth.invalid_keyKey not recognized
429rate_limit.exceededToo many requests
429concurrent_limit.exceeded5 active jobs already running
507storage.insufficientNot enough free disk space

Get Extraction Results

GET /api/v1/extractions/{id}

Retrieve full results for a specific extraction.

Status progression: queuedprocessingcompleted | failed

When status is failed, error_message has a human-readable description and error_code has a structured code (see Error Codes).

Response — 200 OK

{
  "status": "success",
  "data": {
    "id": 1,
    "filename": "application.pdf",
    "form_type": "ACORD 125",
    "carrier": "Example Insurance Co",
    "insurer": "National Underwriters",
    "product": "Employment Practices Liability",
    "application_name": "EPL Application",
    "application_id": "EPL-APP-2024",
    "carrier_product_name": "Example EPL Shield",
    "application_version": "Rev. 01/2024",
    "field_count": 42,
    "duration_ms": 8432,
    "job_status": "completed",
    "error_message": null,
    "error_code": null,
    "api_key_id": 3,
    "created_at": "2026-02-14T12:00:00",
    "fields": [
      {
        "field_name": "Insured Name",
        "value": "Acme Corp",
        "section": "General Information",
        "page": 1
      }
    ]
  }
}

Errors

StatusCodeCause
404resource.not_foundExtraction ID invalid

List Extractions

GET /api/v1/extractions

List past extractions, most recent first. Supports pagination.

Query Parameters

ParamTypeDefaultDescription
pageint1Page number (1-based)
page_sizeint50Items per page (max 200)

Response — 200 OK

{
  "status": "success",
  "data": {
    "extractions": [
      {
        "id": 1,
        "filename": "application.pdf",
        "form_type": "ACORD 125",
        "carrier": "Example Insurance Co",
        "field_count": 42,
        "duration_ms": 8432,
        "job_status": "completed",
        "api_key_id": 3,
        "created_at": "2026-02-14T12:00:00"
      }
    ],
    "total": 1,
    "page": 1,
    "page_size": 50,
    "total_pages": 1
  }
}

Download Extraction as CSV

GET /api/v1/extractions/{id}/csv

Get the extracted data formatted as CSV.

The CSV includes a Metadata section (form type, carrier, insurer, product, application name, application ID, carrier product name, application version) followed by all extracted fields with columns: Section, Field Name, Value, Page.

Response — 200 OK

{
  "status": "success",
  "data": {
    "csv": "Section,Field Name,Value,Page\nMetadata,Form Type,ACORD 125,\n...",
    "filename": "application_extracted.csv"
  }
}

Create API Key

Admin rate limit: Key management endpoints are limited to 5 requests per 60 seconds per key.
POST /api/v1/keys?name=My+Key+Name

Generate a new API key. The raw key is returned only once — store it securely.

Query Parameters

ParamTypeRequiredDescription
namestringYesLabel for this key

Response — 201 Created

{
  "status": "success",
  "data": {
    "name": "My Key Name",
    "key": "pdfx_a1b2c3d4e5f6...",
    "created_at": "2026-02-14T12:00:00"
  }
}

List API Keys

GET /api/v1/keys

List all API keys with metadata. Never returns the full key — only the 8-character prefix.

Response — 200 OK

{
  "status": "success",
  "data": [
    {
      "id": 1,
      "name": "My Key Name",
      "key_prefix": "pdfx_a1b",
      "is_active": true,
      "created_at": "2026-02-14T12:00:00",
      "last_used_at": "2026-02-14T14:30:00"
    }
  ]
}

Revoke API Key

DELETE /api/v1/keys/{id}

Soft-revoke an API key. It will no longer authenticate. The record is kept for audit purposes.

Response — 200 OK

{
  "status": "success",
  "data": {
    "id": 1,
    "revoked": true
  }
}

Typical Workflow

  1. Create an API key
    POST /api/v1/keys?name=MyApp
    Save the returned key — it's shown only once.
  2. Upload a PDF
    POST /api/v1/extract with X-API-Key header
    You'll get back a job ID immediately.
  3. Poll for results
    GET /api/v1/extractions/{id}
    Repeat until job_status is completed or failed.
  4. Download CSV (optional)
    GET /api/v1/extractions/{id}/csv

cURL Examples

Extract a PDF

curl -X POST https://api.prowritersservices.com/api/v1/extract \
  -H "X-API-Key: pdfx_your_key_here" \
  -F "file=@application.pdf"

Check extraction status

curl https://api.prowritersservices.com/api/v1/extractions/1 \
  -H "X-API-Key: pdfx_your_key_here"

Download CSV

curl https://api.prowritersservices.com/api/v1/extractions/1/csv \
  -H "X-API-Key: pdfx_your_key_here"

Error Codes

HTTP Error Codes

CodeHTTPDescription
auth.missing_key401No X-API-Key header provided
auth.invalid_key401API key not recognized
auth.revoked_key403API key has been revoked
validation.error400Invalid request or file
resource.not_found404Extraction or key not found
rate_limit.exceeded429Too many requests, try again shortly
concurrent_limit.exceeded429Too many active extractions for this key
storage.insufficient507Not enough free disk space to accept uploads
server.internal_error500Unexpected server error

Extraction Error Codes

These appear in the error_code field of a failed extraction (via GET /api/v1/extractions/{id}), not as HTTP response codes:

CodeDescription
extraction.api_errorClaude API call failed
extraction.token_limitInput exceeded Claude's token limit
extraction.invalid_pdfPDF could not be read or parsed
extraction.timeoutChunk processing exceeded 10-minute timeout
extraction.no_structured_dataClaude returned no structured extraction results
extraction.internalUnexpected error during extraction

Health Check

GET /health

No authentication required. Returns server and database/Redis connectivity status.

Response — 200 OK

{
  "status": "healthy",
  "checks": {
    "database": "connected",
    "redis": "connected"
  },
  "version": "1.0"
}