Skip to main content
On July 17, 2026, the response format of the v2 APIs changed. This page lists every field-level change for developers who built against the v2 APIs before that date. The endpoints and authentication are unchanged, and the Markdown output format is the same. If you are migrating from the Parse v1 API instead, see the Migration Guide. In this reference:

Parse API Changes

The /v2/parse response is now one tree instead of two, and the values inside it changed:
  • Grounding is inline on each block. The top-level grounding field is removed. Every node in structure below the root now carries its own self-contained grounding object with the node’s page, Markdown range, and bounding box. Walk one tree instead of two, or flatten it into a map by block id. See Grounding. Before:
    After:
  • Spans are now ranges. The span arrays ([start, end]) are replaced by range objects ({"start": n, "end": n}), still [start, end) offsets into the top-level markdown string in Unicode code points. The new metadata.range_units field declares the units ("unicode_codepoints").
  • Bounding boxes are normalized. Boxes change from integer-pixel arrays ([left, top, right, bottom]) to objects ({"xmin": n, "ymin": n, "xmax": n, "ymax": n}) in normalized page coordinates: fractions of the page’s width and height from 0 to 1, with at most 8 decimal places. The dpi request option and the page width, height, and dpi response fields are removed; to convert a box to pixels, multiply by the dimensions of whatever rendering of the page you draw on. See Working with Boxes.
  • Page numbers are 1-indexed. Page 1 is the document’s first page, everywhere: in each node’s grounding.page, in metadata.failed_pages, and in the options.pages request selector, where 0 now returns HTTP 422. Table row and col positions stay 0-indexed.
  • The parts array is now atomic_grounding. Each entry has the same {page, range, box} shape as a block’s own grounding. The field appears only on leaf blocks (every type except table), and the grounding.parts request option is replaced by atomic_grounding. See Atomic Grounding.
  • Block ids are semantic. Ids follow the format <type>-<index>, with a per-type counter in reading order: text-0 is the document’s first text block and table_cell-3 is the fourth cell of its first table. Ids are stable within a response but not across re-parses.
  • The blocks.<type>.caption option is renamed to blocks.<type>.markdown. Same behavior, new name. Requests that send an unknown or legacy option key (dpi, grounding, blocks.<type>.caption) return HTTP 422.
  • New inline_markdown option. Set options.inline_markdown to true to have every structure node (document, page, block, and table cell) carry its own markdown slice, so you don’t have to slice the top-level string by range. See Request Options.
  • The password field moved into options. Password-protected files remain unsupported; providing options.password returns HTTP 422.
  • Metadata renames and additions. The markdown_chars field is renamed to output_markdown_chars, and the new range_units and openapi_spec fields declare the range units and the spec URL. See Metadata.
  • Rotated images are parsed upright. Images with EXIF orientation metadata (such as phone photos) are now normalized before parsing, so bounding boxes align with the image as a viewer sees it. Previously, boxes for rotated images were computed in the unrotated frame and appeared misaligned.

Extract API Changes

The /v2/extract response follows the same conventions:
  • Spans are now ranges. In extraction_metadata, each field’s spans array of [start, end] pairs is replaced by a ranges array of {"start": n, "end": n} objects. See Extract API Response. Before:
    After:
  • The version metadata field is renamed to model_version. The metadata.doc_id field is unchanged.
  • New range_units and openapi_spec metadata fields. Same meaning as on the Parse response.
  • Billing is verifiable from the response. Credit consumption for input is now based on exactly the Markdown you submit, and metadata adds input_markdown_chars and output_extraction_chars, the input and output bases of the charge. See Credit Consumption.
  • The credit_usage metadata field is removed. Credits are reported once, in metadata.billing.total_credits, matching the Parse response.
  • Extract Jobs can save output to your storage. Pass output_save_url when creating a job to have the result delivered to a presigned URL; the poll response then reports output_url instead of result. See Save Extraction Output to a URL.
  • Extract reports partial success. The response carries new warnings and schema_violation_error fields, and a synchronous request returns HTTP 206 instead of 200 when either is set (job polls stay 200, with the signals inside result). See Status 206.

Jobs and Job ID Changes

  • The Parse Jobs poll response uses the same envelope as Extract Jobs. On GET /v2/parse/jobs/{job_id}, the data field is renamed to result, and failure_reason is replaced by an error object with a code and message. Timestamps are ISO-8601 strings (previously Unix seconds), a completed_at timestamp is added, and the top-level version and metadata fields are removed (the result’s metadata lives inside result). The cancelled status is retired. See Parse Asynchronously.
  • The create response reports the job’s status. Creating a job (POST /v2/parse/jobs or POST /v2/extract/jobs) returns job_id, status, and created_at. The status is normally pending, but a job that finishes very quickly can already report a terminal status.
  • The list endpoint is paginated. On GET /v2/parse/jobs, each row now reports completed_at and model_version, and the response pages with page, page_size, and has_more. Filter with the status query parameter.
  • Job ids have one format across the v2 APIs. New job ids follow <service>-<id> (for example, parse-01k04g2b8xv9q3m5n7r2sd4tfe), and the same id appears on the synchronous response, the job-create response, and every poll. Treat ids as opaque strings; existing ids remain valid.

One Error Format

Errors across the v2 APIs now share one shape and one validation status code:
  • Every error body is {"code": "...", "message": "..."}. The code is a stable snake_case identifier (for example, validation_error, unknown_model_version, invalid_url, invalid_api_key, rate_limit_exceeded); the message is human-readable. This replaces the previous detail strings and validation arrays, and matches the error object on failed async jobs.
  • Request-validation failures return 422. Unknown model versions and invalid URLs previously returned 400; they now return 422 like every other validation failure.
  • Unknown request options return 422. Option keys the API does not recognize are rejected instead of ignored, including the retired legacy parse options (dpi, grounding, blocks.<type>.caption).
See Troubleshoot Parsing and Troubleshoot Extraction.