---
title: "Extract structured data from any document"
method: POST
path: "/any/to-struct"
tags: ["Smart Conversion"]
---

# Extract structured data from any document

`POST /any/to-struct`

## Request

- Base URL: `https://api.filegraph.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.filegraph.ai/any/to-struct`
- Auth: API key in header `X-API-Key`

Extract structured data from documents into JSON that matches your data structures.

## The Pattern

1. Define your data structure with descriptions (Pydantic model, Go struct, TypeScript interface)
2. Generate JSON Schema from your model (e.g., `model_json_schema()` in Pydantic)
3. Flatten the schema to remove `$ref`/`$defs` (required by this API)
4. Upload a document
5. Parse `extracted_data` directly into your type

## Example (Python with Pydantic)

```python
import httpx
import json
from pydantic import BaseModel, Field

# 1. Define your type with descriptions (required for LLM extraction)
class ContactInfo(BaseModel):
    name: str | None = Field(None, description="Person's full name")
    email: str | None = Field(None, description="Email address")

# 2. Helper to flatten Pydantic's JSON Schema for API compatibility
def flatten_schema(schema: dict) -> dict:
    """Convert model_json_schema() output to API-compatible format.

    Resolves $ref/$defs and simplifies anyOf[T, null] to T.
    """
    defs = schema.pop("$defs", {})

    def resolve(obj):
        if isinstance(obj, dict):
            if "$ref" in obj:
                ref_name = obj["$ref"].split("/")[-1]
                return resolve(defs[ref_name])
            if "anyOf" in obj:
                non_null = [t for t in obj["anyOf"] if t.get("type") != "null"]
                if len(non_null) == 1:
                    result = {k: v for k, v in obj.items() if k not in ("anyOf", "default")}
                    result.update(resolve(non_null[0]))
                    return result
            return {k: resolve(v) for k, v in obj.items()}
        if isinstance(obj, list):
            return [resolve(i) for i in obj]
        return obj

    result = resolve(schema)
    result.pop("title", None)
    result.pop("required", None)
    return result

# 3. Generate flattened schema from your model
schema = flatten_schema(ContactInfo.model_json_schema())

# 4. Call the API
with open("business_card.png", "rb") as f:
    response = httpx.post(
        "https://api.filegraph.ai/any/to-struct",
        headers={"X-API-Key": "your-api-key"},
        files={"file": f},
        data={"schema": json.dumps(schema)},
    )

# 5. Parse directly into your type
contact = ContactInfo(**response.json()["extracted_data"])
```

## Why flatten_schema?

Pydantic's `model_json_schema()` produces schemas with `$ref` and `$defs` for nested models,
and `anyOf[T, null]` for optional fields. This API requires a flat schema without these.
The helper resolves references and simplifies optional types.

## Supported JSON Schema Types
- **Primitives**: `string`, `integer`, `number`, `boolean`
- **Formats**: `email`, `uri`, `date`, `date-time`, `phone`
- **Collections**: `array` (with `items`)
- **Nested**: `object` with `properties` (max 3 levels deep)
- **Enums**: `enum` for fixed value lists

## Rate Limiting
This endpoint has 3x rate limit weight due to LLM processing costs.

## Response `200`

Structured data successfully extracted

- StructuredExtractionResponse: Response for structured data extraction endpoint.
  - `extracted_data` object, required: Extracted data matching the provided schema
  - `raw_text` string, required: Raw text extracted from the document before LLM processing
  - `format_detected` string, required: Detected input format (e.g., 'pdf', 'docx', 'image')
  - `used_ocr` boolean, required: Whether OCR was used for text extraction
  - `model_used` string, required: LLM model used for extraction
  - `processing_time_ms` integer, required: Total processing time in milliseconds
  - `tables` ExtractedTable[], nullable: Extracted tables from the document (only included if include_tables=true)
    - `table_index` integer, required: Zero-indexed position of this table in the document
    - `page` integer, nullable: Page number (1-indexed) where table appears
    - `caption` string, nullable: Table caption or title if detected
    - `columns` TableColumn[], required: Column metadata with names and inferred types
      - `name` string, required: Column header name
      - `inferred_type` 'string' | 'number' | 'boolean' | 'date' | 'mixed', required: Inferred data type based on column values
    - `rows` object[], required: Table data as array of row objects, keys are column names
    - `row_count` integer, required: Number of data rows
    - `truncated` boolean: Whether the table was truncated due to size limits
    - `truncation_reason` string, nullable: Reason for truncation if applicable
    - `original_row_count` integer, nullable: Original row count before truncation
    - `original_column_count` integer, nullable: Original column count before truncation
  - `table_count` integer, nullable: Number of tables extracted (only included if include_tables=true)

## Other responses

- `400`: Invalid schema
- `401`: Invalid or missing API key
- `402`: Insufficient credits for this operation
- `413`: File exceeds size limit
- `422`: LLM extraction failed
- `500`: Unexpected internal error
- `503`: LLM not configured

---

[API](https://skmtc.dev/filegraph/apis/docconvert-api.md) · [All operations](https://skmtc.dev/filegraph/apis/docconvert-api/llms.txt) · [OpenAPI document](https://skmtc.dev/filegraph/apis/docconvert-api/revisions/436255c85bd4?raw)
