API Reference

DocuParse API Documentation

Extract structured JSON from receipts and invoices with a single HTTP request.

Quickstart

You can be sending requests in under 5 minutes:

  1. 1. Create a free account at DocuParse.
  2. 2. Go to Dashboard → API Keys and create a new key. Copy it immediately — it's shown only once.
  3. 3. Send your first request using the example below.
Quick test (cURL)
curl -X POST "https://YOUR_DOMAIN/api/v1/extract" \
  -H "Authorization: Bearer dex_your_api_key_here" \
  -F "file=@receipt.pdf"

Authentication

All API requests must include your API key as a Bearer token in the Authorization header.

Authorization: Bearer dex_your_api_key_here

Security warning

Never include your API key in browser/frontend code. API keys must be stored server-side only (environment variables, secrets manager). Treat them like passwords.

POST /api/v1/extract

Accepts a receipt or invoice file and queues it for async processing. Use GET /api/v1/documents/[id] to poll for the result.

Request

ParameterTypeRequiredDescription
fileFile (form-data)YesPDF or CSV. Max 10MB.

Headers

HeaderValue
AuthorizationBearer <API_KEY>
Content-Typemultipart/form-data (set automatically by HTTP clients)

Request examples

All examples below use server-side code only. Never call this endpoint from browser JavaScript with a secret API key.

cURL

curl -X POST "https://YOUR_DOMAIN/api/v1/extract" \
  -H "Authorization: Bearer dex_your_api_key" \
  -F "file=@receipt.pdf"

Node.js (using built-in fetch)

// IMPORTANT: Run this server-side only. Never expose your API key in the browser.
// Store API_KEY in an environment variable.

const fs = require("fs");
const path = require("path");

async function extractDocument(filePath) {
  const apiKey = process.env.DOCUEXTRACT_API_KEY; // e.g. "dex_..."

  const form = new FormData();
  const fileBuffer = fs.readFileSync(filePath);
  const filename = path.basename(filePath);
  const blob = new Blob([fileBuffer]);
  form.append("file", blob, filename);

  const res = await fetch("https://YOUR_DOMAIN/api/v1/extract", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
    },
    body: form,
  });

  const data = await res.json();

  if (!data.success) {
    // data.error.code is one of: MISSING_API_KEY, INVALID_API_KEY,
    // REVOKED_API_KEY, LIMIT_EXCEEDED, UNSUPPORTED_FILE_TYPE,
    // FILE_TOO_LARGE, EXTRACTION_FAILED, INTERNAL_ERROR
    throw new Error(`[${data.error.code}] ${data.error.message}`);
  }

  return data;
}

// Usage
extractDocument("./receipt.pdf")
  .then((result) => {
    console.log("Document ID:", result.document_id);
    console.log("Status:", result.status);
  })
  .catch(console.error);

Next.js API Route (server-side)

// app/api/upload-receipt/route.ts
// This is a Next.js App Router API route that proxies the call to DocuParse.
// The secret API key stays on the server.

import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
  const apiKey = process.env.DOCUEXTRACT_API_KEY;
  if (!apiKey) {
    return NextResponse.json({ error: "Server misconfiguration." }, { status: 500 });
  }

  // Forward the multipart form from the client to DocuParse
  const formData = await req.formData();
  const file = formData.get("file");

  if (!file || !(file instanceof File)) {
    return NextResponse.json({ error: "No file provided." }, { status: 400 });
  }

  const upstream = new FormData();
  upstream.append("file", file);

  const res = await fetch("https://YOUR_DOMAIN/api/v1/extract", {
    method: "POST",
    headers: { Authorization: `Bearer ${apiKey}` },
    body: upstream,
  });

  const data = await res.json();

  if (!data.success) {
    return NextResponse.json(
      { error: data.error.message, code: data.error.code },
      { status: res.status }
    );
  }

  return NextResponse.json(data);
}

Python (requests)

# IMPORTANT: Run server-side only. Store your key in an environment variable.
import os
import requests

def extract_document(file_path: str) -> dict:
    api_key = os.environ["DOCUEXTRACT_API_KEY"]  # e.g. "dex_..."

    with open(file_path, "rb") as f:
        response = requests.post(
            "https://YOUR_DOMAIN/api/v1/extract",
            headers={"Authorization": f"Bearer {api_key}"},
            files={"file": (os.path.basename(file_path), f)},
        )

    data = response.json()

    if not data.get("success"):
        # Structured error: data["error"]["code"] and data["error"]["message"]
        raise RuntimeError(f"[{data['error']['code']}] {data['error']['message']}")

    return data


# Usage
result = extract_document("receipt.pdf")
print("Merchant:", result["data"]["merchant"])
print("Total:", result["data"]["total"])
print("Status:", result["status"])

Raw fetch (browser UNSAFE — for demonstration only)

Do not use in production browser code.

This example is for reference only. In production, proxy the request through your own server so the API key is never exposed to the client.

// DEMO ONLY — DO NOT EXPOSE API KEYS IN BROWSER CODE
// Use this pattern only in a backend/server-side context.

const form = new FormData();
form.append("file", fileInput.files[0]);

const res = await fetch("https://YOUR_DOMAIN/api/v1/extract", {
  method: "POST",
  headers: {
    // In real production: call YOUR backend instead, never put the key here
    Authorization: "Bearer dex_your_api_key",
  },
  body: form,
});

const data = await res.json();
if (data.success) {
  console.log(data.data);
} else {
  console.error(data.error.code, data.error.message);
}

Response format

Queue response (200)

{
  "success": true,
  "document_id": "doc_clx7abc123",
  "status": "queued",
  "message": "Document queued for extraction."
}

Poll result endpoint

curl -X GET "https://YOUR_DOMAIN/api/v1/documents/doc_clx7abc123"   -H "Authorization: Bearer dex_your_api_key"

Completed response (200)

{
  "success": true,
  "document_id": "doc_clx7abc123",
  "status": "completed",
  "data": {
    "merchant": "Office Depot",
    "currency": "USD",
    "subtotal": 42.00,
    "tax": 3.50,
    "total": 45.50
  }
}

Failed response (422)

{
  "success": false,
  "document_id": "doc_clx7abc123",
  "status": "failed",
  "error": {
    "code": "EXTRACTION_FAILED",
    "message": "Document extraction failed."
  }
}

Error codes

CodeHTTP statusDescription
MISSING_API_KEY401No Authorization header provided.
INVALID_API_KEY401The key does not exist.
REVOKED_API_KEY401The key was revoked.
LIMIT_EXCEEDED429Monthly document limit reached.
UNSUPPORTED_FILE_TYPE422File is not JPG, PNG, or PDF.
FILE_TOO_LARGE422File exceeds 10MB.
NO_FILE_PROVIDED422No file field in the request.
EXTRACTION_FAILED500Extraction pipeline failed. Retry or contact support.
INTERNAL_ERROR500Unexpected server error.

Rate limits

Rate limits apply per API key. Document limits are counted monthly and reset at the start of each calendar month.

PlanMonthly documentsWhen exceeded
Free50Requests return LIMIT_EXCEEDED (429)
Starter500Overage charged at $0.10/doc
Pro5,000Overage charged at $0.05/doc

If you need a higher limit for a specific use case, contact support.

Best practices

  • Store keys server-side only

    Never include your API key in browser JavaScript, mobile app source code, or public repositories. Use environment variables.

  • Handle errors explicitly

    Check the error.code field in failure responses to route errors to the right handling logic. Don't rely solely on HTTP status codes.

  • Rotate keys periodically

    Create new API keys and revoke old ones regularly. Each key should have a descriptive name (e.g. "production-app", "staging") for easy identification.

  • Use the document_id for deduplication

    Each successful extraction returns a unique document_id. Store it to avoid re-processing the same file.

  • Check confidence scores

    The confidence.overall field indicates extraction reliability. Consider flagging results with overall < 0.7 for manual review.

  • Compress images before uploading

    High-resolution images can be compressed without losing relevant text data. Keeping files under 2MB improves processing time.

Webhooks

Coming soon

Webhook delivery will allow you to receive extraction results via HTTP POST to your endpoint instead of polling. You will be able to configure a webhook URL and secret in Dashboard → Settings.

Planned events: extraction.completed extraction.failed limit.warning