Skip to content

Importing PDFs with OMR

Flat's Optical Music Recognition (OMR) turns a PDF of sheet music into a fully editable Flat score. You can trigger an OMR import directly from our REST API and follow its progress until the score is ready.

Unlike importing a MusicXML or MIDI file — which is synchronous — OMR processing takes some time. The API therefore handles it as an asynchronous task:

  1. You start the import with POST /scores. The API replies immediately with a task.
  2. You poll the task until it is done.
  3. Once done, you retrieve (and optionally export) the newly created score.

In this page:

Looking for the end-user OMR feature instead? See the OMR help center article and its changelog.

Prerequisites

  • Authentication. A Personal Access Token works nicely to get started quickly. If you are building an OAuth2 app, request the scores scope (to create the score) and the tasks.readonly scope (to poll the task).
  • File format. Only PDF (application/pdf) files go through OMR.
  • Encoding. Binary PDF data must be Base64-encoded and sent in the data property, with dataEncoding set to base64.
  • Opt in to tasks. You must set supportsTasks: true in the request. Without it, a PDF import is rejected.

Step 1 — Start the import

Create the score with POST /scores, providing the Base64-encoded PDF and supportsTasks: true:

bash
curl -X POST 'https://api.flat.io/v2/scores' \
  -H 'Authorization: Bearer <my_api_personal_access_token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "title": "My imported score",
    "filename": "my-sheet-music.pdf",
    "data": "<base64-encoded-pdf>",
    "dataEncoding": "base64",
    "supportsTasks": true
  }'

Because the PDF requires OMR, the API responds with 202 Accepted and a task instead of the score:

json
{
  "id": "5e0a59c0f1e6b8000868e0c1",
  "type": "import-omr",
  "state": "created",
  "progress": {
    "percent": 0
  },
  "creationDate": "2026-06-16T09:24:00.000Z"
}

Keep the task id — you will use it to follow the import.

Branch on the HTTP status

MusicXML and MIDI imports are processed synchronously and still return 200 OK with the full score. A PDF import returns 202 Accepted with a task. Have your client branch on the status code: 200 → the score is ready, 202 → follow the task flow below.

Step 2 — Poll the task

Fetch the task with GET /tasks/{task} until it completes:

bash
curl -H 'Authorization: Bearer <my_api_personal_access_token>' \
  https://api.flat.io/v2/tasks/5e0a59c0f1e6b8000868e0c1

The task moves through the following states:

stateMeaning
createdThe task is queued and waiting to be processed.
doingOMR processing is in progress (see progress.percent).
doneThe score has been imported — the score field is set.
errorProcessing failed — see result.error.

The fields you care about while polling:

FieldDescription
idUnique identifier of the task.
typeimport-omr for an OMR import.
stateCurrent state of the task (see table above).
progress.percentProgression of the task, from 0 to 100.
scoreThe unique identifier of the imported score (set once state is done).
result.errorA human-readable error message when state is error.

A completed task looks like:

json
{
  "id": "5e0a59c0f1e6b8000868e0c1",
  "type": "import-omr",
  "state": "done",
  "score": "5e0a5b2cf1e6b8000868e0d4",
  "revision": "5e0a5b2cf1e6b8000868e0d5",
  "progress": {
    "percent": 100
  },
  "creationDate": "2026-06-16T09:24:00.000Z",
  "doneDate": "2026-06-16T09:25:12.000Z"
}

Poll every few seconds rather than in a tight loop, and respect our rate limits.

Step 3 — Retrieve the imported score

Once the task is done, read the score field for the new score identifier, then fetch the full score details with GET /scores/{score}:

bash
curl -H 'Authorization: Bearer <my_api_personal_access_token>' \
  https://api.flat.io/v2/scores/5e0a5b2cf1e6b8000868e0d4

From there, the score behaves like any other Flat score. You can also export it to other formats — for example MusicXML, MP3, or MIDI — using the score export endpoints. Exporting follows the same asynchronous task pattern as the import: create the export task with POST /scores/{score}/revisions/{revision}/{format}/task, poll GET /tasks/{task}, then download the result from the task's result.url once it is done.

Error handling

StatusWhen it happens
400Bad request — for example a missing supportsTasks: true, or an invalid or password-protected PDF.
402The account is over quota, the OMR feature is not included in the plan, or there are not enough credits.

In addition, a task may finish in the error state. When that happens, inspect result.error for a description of what went wrong. See the Errors page for the general error format.

Usage & billing

OMR imports consume the credits available on the user's Flat account. If you need a custom implementation, higher volumes, or have specific requirements, reach out to us at developers@flat.io — we're happy to discuss the best setup for your use case.

We are also working on a dedicated import API that will offer more options to control imports. Contact us if you'd like to be kept in the loop.

Copyright © Tutteo Limited