Skip to main content

FastOCR API

Upload a PDF or an image. Poll for completion. Download the extracted text or a searchable PDF.

Get API key

20 free pages a month, then Page Packs from $4.99 for 500 pages.

What is the FastOCR REST API?

The FastOCR REST API extracts text from scanned and digital PDFs and from images in more than 100 languages. It runs an asynchronous five-step flow. Create a document. Upload the file to a presigned S3 URL. Start processing. Poll the document status. Download the output as plain text, or as a searchable PDF with an embedded text layer. It accepts PDF files up to 1 GiB, and PNG, JPG, JPEG, GIF, WebP and BMP images up to 20 MiB. An image counts as one page. Any signed-in FastOCR account can create an API key and start right away. The Free plan includes 20 free pages per month, shared with PDFs on the website. Paid plans share their Document OCR allowance with the API too. You buy more pages as Page Packs on the pricing page. Page Packs work on the website too.

Python SDKfastocr-sdk 0.3.0PyPIGitHub

Docs menu
On this page

Quickstart

  1. Create an API key

    Sign in and create a key in the developer console. You accept the API terms with your first key. The Free plan includes 20 free pages per month, shared with PDFs on the website. Paid plans share their Document OCR allowance with the API too. Every example reads the key from FASTOCR_API_KEY.

    Terminalbash
    pip install fastocr-sdk
    export FASTOCR_API_KEY="your_api_key"

    The first line installs the Python SDK, fastocr-sdk. It is not related to the fastocr package on PyPI. Node.js 18+ and cURL need nothing extra.

  2. Run the snippet

    Each tab extracts the text of scan.pdf and saves or prints it.

    Python
    from fastocr_sdk import FastOCR
    
    # Reads the key from the FASTOCR_API_KEY environment variable.
    with FastOCR() as client:
        text = client.extract_text("scan.pdf")
        print(text)
  3. Read the result

    The Node.js and HTTP tabs poll GET /v1/documents/{id} until the document finishes. A completed document looks like this:

    200 OKJSON
    {
      "id": "3f9c2b1e-7d54-4a8e-9b0a-5c1e8d2f6a47",
      "status": "completed",
      "external_id": "INV-1001",
      "pages_billed": 10,
      "pages_total": 10,
      "pages_processed": 10,
      "is_truncated": false,
      "outputs": {
        "text": { "available": true },
        "pdf": { "available": true }
      }
    }

    Then GET /v1/documents/{id}/output?format=text (or format=pdf) returns a download link that works for one hour. The Document OCR reference describes each request.

Packages

Prefer a package to raw HTTP? These call the same API with the same key.

fastocr-sdk

The Python client for the API.

pip install fastocr-sdk

Setup →PyPI

fastocr-mcp

Lets Claude, Cursor and other MCP clients OCR a local PDF or image.

claude mcp add fastocr -e FASTOCR_API_KEY=your_api_key -- uvx fastocr-mcp

Setup →PyPI

langchain-fastocr

A LangChain document loader that returns one Document per scanned file.

pip install langchain-fastocr

Setup →PyPI

llama-index-readers-fastocr

A LlamaIndex reader that returns one Document per scanned file.

pip install llama-index-readers-fastocr

Setup →PyPI

Getting access

Any signed-in FastOCR account can create an API key and start right away. The Free plan includes 20 free pages per month, shared with PDFs on the website. Paid plans share their Document OCR allowance with the API too.

  1. Sign in and create an API key in the developer console. You accept the API terms with your first key.
  2. Run the quickstart with your key in FASTOCR_API_KEY.
  3. When you need more document pages, buy a Page Pack on the pricing page.

Pack sizes and rates are on the pricing page. A document that runs out of pages fails with buy_pages. After you buy pages, call /start again on the same document.

API

The Document OCR API has five steps. Create a document, upload the PDF or image, start processing, poll for completion, and download the result.

Document OCR

Upload a PDF or an image, run OCR, and get extracted text or a searchable PDF. The API supports 100+ languages.

View reference

Authentication

Every request sends your API key in the Authorization header as Bearer <key>. Create API keys in the developer console. Each API key starts with fok_live_.

Python
from fastocr_sdk import FastOCR

client = FastOCR(api_key="fok_live_abc123...")

The API gateway returns authentication failures before the request reaches the API.

  • The body always has a message. On newer deployments it also has an error object with type: "gateway_error".
  • A request with no Authorization header returns 401 (code unauthorized).
  • A header with an unknown, malformed, or revoked API key returns 403, not 401 (code access_denied).
  • If error is absent, use the HTTP status and message.
  • The gateway caches API key checks for up to 60 seconds. A revoked API key can keep working for that time.

Pagination

List endpoints return one page of results and an opaque cursor. Send the cursor back to get the next page.

ParameterlimitDefault20DescriptionAn integer from 1 to 100. It sets the number of items on each page.
ParametercursorDefault—DescriptionAn opaque string. Use next_cursor from the previous response.
ParameterstatusDefault—DescriptionDocument list only. One of awaiting_upload, processing, completed, partial, or failed.
Parameterupdated_sinceDefault—DescriptionReturns documents whose status changed at or after this time. Send an ISO 8601 date or datetime, for example 2026-10-01T00:00:00Z. A value with no offset is UTC.
Python
from fastocr_sdk import FastOCR

with FastOCR() as client:
    cursor = None
    while True:
        page = client.documents.list(limit=100, cursor=cursor)
        for document in page.documents:
            print(document.id, document.status)
        cursor = page.next_cursor
        if cursor is None:
            break

The API lists documents newest first in the documents array. When next_cursor is null, there are no more pages.

Each page holds up to limit documents that match your filters. On a very long list, a page can be shorter while next_cursor is still set. Follow the cursor until it is null. A cursor expires 24 hours after the API issues it.

The updated_since filter

updated_since accepts an ISO 8601 date or datetime. The value can end in Z, have any offset, or have no offset. The API reads a value with no offset as UTC.

A document matches when its last status change is at or after the value. If the status never changed, the creation time applies. A date alone, such as 2026-10-01, works.

The API does not reject a value that is not a date. It compares that value as text.

Idempotency

Create endpoints need an Idempotency-Key header. The API matches the header name without regard to case.

A client can retry a request with the same key and body within about 24 hours. The API then returns the original 201. If the original upload_url expired while the document is still awaiting_upload, the response carries a fresh upload_url and expires_at.

The API compares the body on filename, size_bytes, sha256, and external_id. If the body is different, the API returns 409 idempotency_key_conflict.

Python
from fastocr_sdk import FastOCR

with FastOCR() as client:
    created = client.documents.create(
        "scan.pdf", 1048576, idempotency_key="invoice-2026-10-001"
    )

Limits

  • Files: a PDF up to 1 GiB (1,073,741,824 bytes), or a PNG, JPG, JPEG, GIF, WebP or BMP image up to 20 MiB (20,971,520 bytes). An image counts as one page.
  • Concurrency: an account can process 2 documents at the same time. An account with an API contract, or with 10,000 or more pages bought, can process 10. Very large documents may take longer to start.
    A /start request over the limit returns 429 concurrency_limit with a Retry-After header. The document stays awaiting_upload. Wait the Retry-After seconds, then call POST /start again.
  • Request rate: each API key and each source address has its own request rate limit on /v1, in addition to the shared throttle of the API gateway. A request above a limit returns 429 with the same body shape as the authentication errors (code throttled when the gateway produced it; a per-key or per-address 429 has message only and a Retry-After header). Wait the Retry-After seconds, or back off, then retry.
  • Pages: The Free plan includes 20 free pages per month, shared with PDFs on the website. Paid plans share their Document OCR allowance with the API too. Page Packs add pages to the same balance, and they work on the website too. Check what is left with GET /v1/balance.
    The API reserves pages when it knows the real page count. This is after /start returns. If fewer pages remain than the PDF has, the document finishes as partial. The API then processes the first pages only. If no pages remain, the document fails with buy_pages.

Errors

A document that fails during processing returns HTTP 200 with the status failed and an error object. The HTTP status describes the request. It does not describe the work.

  • Match on code. Do not match on message. FastOCR can change messages without notice.
  • FastOCR will add new codes. Treat an unknown code as processing_failed.
  • Only buy_pages lets you start a failed document again. A document that runs out of pages fails with buy_pages. After you buy pages, call /start again on the same document.

Document failure codes

Failed documentJSON
{
  "id": "3f9c2b1e-7d54-4a8e-9b0a-5c1e8d2f6a47",
  "status": "failed",
  "external_id": null,
  "error": {
    "type": "processing_error",
    "code": "timeout",
    "message": "This file took too long to process. Please try again.",
    "retryable": true
  }
}
Codebuy_pagesTypeinsufficient_creditsWhenThe account has no pages left.RetryableNo. Buy a Page Pack. Then call POST /start again on the same document.
Codepassword_protectedTypeinvalid_requestWhenThe file has a password.RetryableNo
Codeunreadable_documentTypeinvalid_requestWhenThe API could not read the PDF.RetryableNo
Codeno_text_detectedTypeinvalid_requestWhenThe API found no readable text in the file.RetryableNo
Codecorrupted_fileTypeinvalid_requestWhenThe file has damage, or the API does not support it.RetryableNo
Codeimage_too_largeTypeinvalid_requestWhenThe image is too large or has too many pixels to read. Upload a smaller copy.RetryableNo
Codetoo_many_pagesTypeinvalid_requestWhenThe file has more pages than one document may hold.RetryableNo
Codeempty_fileTypeinvalid_requestWhenThe file has no pages.RetryableNo
CodetimeoutTypeprocessing_errorWhenProcessing took too long.RetryableYes
Codeout_of_memoryTypeprocessing_errorWhenProcessing used all available memory.RetryableYes
Codeprocessing_failedTypeprocessing_errorWhenAny other failure.RetryableYes

Request errors

A rejected request returns a 4xx or 5xx status. The body has an error object with type, code, message, and request_id. Include the request_id when you contact support.

The type is invalid_request, not_found, conflict, rate_limit, or error.

Request errorJSON
{
  "error": {
    "type": "invalid_request",
    "code": "unsupported_file_type",
    "message": "Only PDF, PNG, JPG, JPEG, GIF, WebP and BMP files are supported",
    "request_id": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef"
  }
}
HTTP400Codeinvalid_jsonTypeinvalid_requestWhenThe request body is not valid JSON, or it is not a JSON object.
HTTP400Codemissing_idempotency_keyTypeinvalid_requestWhenA create request has no Idempotency-Key header.
HTTP400Codeinvalid_idempotency_keyTypeinvalid_requestWhenThe Idempotency-Key header is longer than 255 characters.
HTTP400Codeinvalid_filenameTypeinvalid_requestWhenfilename is missing, is not a string, or is longer than 255 characters.
HTTP400Codeunsupported_file_typeTypeinvalid_requestWhenfilename does not end in .pdf, .png, .jpg, .jpeg, .gif, .webp or .bmp.
HTTP400Codeinvalid_size_bytesTypeinvalid_requestWhensize_bytes is not a positive integer.
HTTP400Codefile_too_largeTypeinvalid_requestWhensize_bytes is more than 1,073,741,824 (1 GiB) for a PDF, or more than 20,971,520 (20 MiB) for an image.
HTTP400Codeinvalid_sha256Typeinvalid_requestWhensha256 is not the base64 encoding of the 32-byte digest.
HTTP400Codeinvalid_external_idTypeinvalid_requestWhenexternal_id is not a string of at most 256 characters.
HTTP400Codeupload_not_foundTypeinvalid_requestWhenA start request came before the file upload.
HTTP400Codesize_mismatchTypeinvalid_requestWhenThe size of the uploaded file is different from size_bytes.
HTTP400Codeinvalid_formatTypeinvalid_requestWhenformat is not text or pdf.
HTTP400Codeinvalid_limitTypeinvalid_requestWhenlimit is not an integer from 1 to 100.
HTTP400Codeinvalid_statusTypeinvalid_requestWhenstatus is not a public document status.
HTTP400Codeinvalid_cursorTypeinvalid_requestWhencursor has the wrong format, or it is more than 24 hours old.
HTTP404Codedocument_not_foundTypenot_foundWhenThis account has no such document.
HTTP404Codeoutput_unavailableTypenot_foundWhenA client requested format=pdf for a document whose searchable PDF could not be generated. The extracted text is still available with format=text.
HTTP409Codeidempotency_key_conflictTypeconflictWhenA request used the key before with a different body.
HTTP409Codedocument_not_startableTypeconflictWhenThe document is completed, partial, or failed with a code other than buy_pages.
HTTP409Codedocument_not_readyTypeconflictWhenA client requested output before the document completed, or the document failed.
HTTP409Codedocument_processingTypeconflictWhenA client sent DELETE for a document that is still in flight.
HTTP429Codeconcurrency_limitTyperate_limitWhenToo many documents are in flight. Wait the Retry-After seconds (30), then call POST /start again.
HTTP500Codenot stableTypeerrorWhenUnexpected server error. Retry with backoff.

Gateway errors

The API gateway returns 401, 403, and 429 errors before the request reaches the API. The body has a message and, on newer deployments, an error object with type: "gateway_error". See Authentication. Treat an unknown code as a new condition for its HTTP status.

HTTP401CodeunauthorizedTypegateway_errorWhenThe request has no Authorization header.
HTTP403Codeaccess_deniedTypegateway_errorWhenThe API key is unknown, malformed, or revoked.
HTTP429CodethrottledTypegateway_errorWhenThe request is above the per-key or per-address rate limit on /v1, or the shared throttle of the API gateway. A per-key or per-address 429 has message only and a Retry-After header. Wait, then retry.
HTTP403Codemissing_authentication_tokenTypegateway_errorWhenThe route does not exist and the request has no Authorization header. With a Bearer API key, the same request returns 403 gateway_error.
HTTPOtherCodegateway_errorTypegateway_errorWhenAny other gateway condition. Use the HTTP status and message.

Handle errors in Python

The SDK raises DocumentFailedError for a failed document and RateLimitError (with retry_after) for a 429. All SDK errors extend FastOCRError.

extract_text raises PartialResultError when the account runs out of pages partway through, so it never returns truncated text without telling you. The request itself succeeded, so PartialResultError has no status_code, and its code is partial_result. Pass allow_partial=True to receive the text of the processed pages instead.

PythonPython
from fastocr_sdk import (
    DocumentFailedError,
    FastOCR,
    FastOCRError,
    PartialResultError,
    RateLimitError,
)

with FastOCR() as client:
    try:
        text = client.extract_text("scan.pdf")
    except PartialResultError as error:
        print(error.pages_processed, error.pages_total)
        text = client.documents.get_text(error.document_id)
    except DocumentFailedError as error:
        print(error.code, error.retryable, error.document_id)
    except RateLimitError as error:
        print(error.retry_after)
    except FastOCRError as error:
        print(error.status_code, error.code, error.request_id)

Versioning and compatibility

/v1 is additive-only. FastOCR adds fields. It never renames, retypes, or deletes a field. Status codes and existing error codes do not change.

New fields, new statuses, and new error codes may appear. Clients must ignore unknown fields. Clients must treat an unknown error code as processing_failed (document errors) or by its HTTP status (request errors).

A breaking change would ship as /v2. /v1 would stay available for at least 6 months after that.

Ready to start?

Create an API key and run the quickstart. 20 free pages a month, then Page Packs from $4.99 for 500 pages.