CookbookExtract JSON in Python

Extract structured data in Python

⚠️
Preview — this feature is being built; the design below is the spec.

Pull invoice fields out of free text into a typed object with a JSON schema.

import json, os
import requests
 
SCHEMA = {
    "type": "object",
    "additionalProperties": False,
    "required": ["supplier", "invoice_number", "issue_date", "total", "currency", "line_items"],
    "properties": {
        "supplier": {"type": "string"},
        "invoice_number": {"type": ["string", "null"]},
        "issue_date": {"type": ["string", "null"], "description": "YYYY-MM-DD"},
        "total": {"type": "string", "description": "Decimal, two places, no symbol"},
        "currency": {"type": "string", "enum": ["EUR", "GBP", "USD", "AED", "OTHER"]},
        "line_items": {"type": "array", "maxItems": 50, "items": {
            "type": "object", "additionalProperties": False, "required": ["description", "amount"],
            "properties": {"description": {"type": "string"}, "amount": {"type": "string"}}}},
    },
}
 
def extract(text: str) -> dict:
    r = requests.post(
        "https://app.syntic.ai/v1/chat/completions",
        headers={"Authorization": f"Bearer {os.environ['SYNTIC_API_KEY']}"},
        json={
            "model": "Amara Quick",
            "stream": False,
            "max_tokens": 1500,
            "response_format": {"type": "json_schema", "json_schema": {"name": "invoice", "schema": SCHEMA, "strict": True}},
            "messages": [
                {"role": "system", "content": "Extract invoice fields. Use null when a field is not stated. Never compute a total that is not printed."},
                {"role": "user", "content": text},
            ],
        },
        timeout=60,
    )
    r.raise_for_status()
    choice = r.json()["choices"][0]
    if choice["finish_reason"] == "length":
        raise RuntimeError("cut off; raise max_tokens")
    if choice["finish_reason"] == "refusal":
        raise RuntimeError(choice["message"]["refusal"])
    return json.loads(choice["message"]["content"])
 
print(extract("ACME Tools Ltd — Invoice 2026-118, 3 Nov 2026. 4x drill bits 36.00; delivery 9.50. Total GBP 45.50"))

Expected output:

{"supplier": "ACME Tools Ltd", "invoice_number": "2026-118", "issue_date": "2026-11-03", "total": "45.50", "currency": "GBP",
 "line_items": [{"description": "4x drill bits", "amount": "36.00"}, {"description": "delivery", "amount": "9.50"}]}

Check sums in your own code (sum(line_items) == total) rather than asking the model to; arithmetic belongs in code. See Design schemas.

Was this page helpful?