// rest api

API Reference

Integrate Data2MD into your workflow with a single HTTP POST. Included with a subscription; up to 200 MB per request.

Authentication

Every request carries a token in an Authorization header. Tokens belong to an account with an active subscription and are created in your dashboard.

Authorization: Bearer d2md_4f1c8ab2d3e076950000000000000000000000000000beef

A token is shown once and stored only as a hash, so treat it like a password. Generating a new one replaces the previous token immediately.

Endpoint

POST https://aiu4.com/api

Content-Type: multipart/form-data

Request fields

FieldTypeRequiredDescription
filefileYesThe file to convert. Max 200 MB.
formatstringNoFile format. Default: pdf. Currently supported: pdf.

Success response 201 Created

preview contains up to ~8 KB of Markdown trimmed at a line boundary. preview_truncated tells you whether the file is larger; fetch the full document via download_url.

{
  "success": true,
  "hash": "a3f9c12b84e7d01",
  "original_name": "report.pdf",
  "format": "pdf",
  "lines": 148,
  "characters": 9342,
  "pages": 3,
  "download_url": "/download?hash=a3f9c12b84e7d01",
  "preview": "# Report Title\n\n...",
  "preview_truncated": false
}

Error response 4xx · 5xx

{
  "success": false,
  "message": "The API is part of the subscription.",
  "code": "plan_required"
}

Every error carries success: false and a readable message. Where a code is listed, branch on the code rather than on the message text.

StatusCodeMeaning
401token_requiredNo Authorization header, or one that is not a bearer token.
401token_invalidThe token does not exist — it was replaced, revoked, or its account was deleted.
403plan_requiredA valid token on an account with no active subscription.
422—The file was rejected: wrong type, over the size limit, or no text layer to extract.
429—The rate limit is spent. Retry-After says how many seconds to wait.
503—Every conversion slot is busy, or this deployment has accounts switched off.

Rate limits

1 request(s) per 30 seconds, counted per account and separately from what the same account converts on the website. Exceeding returns 429 Too Many Requests.

Conversion and /api/me responses carry X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, a Unix timestamp. A 429 adds Retry-After in seconds.

Checking an account

GET https://aiu4.com/api/me

Returns the plan behind a token, the limits that apply to it, and how much of the current window is left.

{
  "success": true,
  "email": "you@example.com",
  "plan": "premium",
  "api_access": true,
  "limits": {
    "max_conversions": 1,
    "window_seconds": 30,
    "max_file_size_bytes": 209715200
  },
  "rate_limit": { "limit": 1, "remaining": 0, "reset": 1767225600 },
  "subscription": { "status": "active", "current_period_end": 1767225600, "cancel_at_period_end": false },
  "token": { "prefix": "d2md_4f1c", "last4": "beef", "created_at": 1764547200, "rotated_at": null, "last_used_at": 1767139200 }
}

It works on any plan and is not rate limited, so a 403 from a conversion can always be explained: read api_access and plan.

Examples

cURL

curl -X POST https://aiu4.com/api \
  -H "Authorization: Bearer $D2MD_TOKEN" \
  -F "file=@document.pdf" \
  -F "format=pdf"

curl https://aiu4.com/api/me \
  -H "Authorization: Bearer $D2MD_TOKEN"

PHP

// stream a file via cURL
$ch = curl_init('https://aiu4.com/api');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('D2MD_TOKEN')],
    CURLOPT_POSTFIELDS     => [
        'file'   => new CURLFile('document.pdf', 'application/pdf', 'document.pdf'),
        'format' => 'pdf',
    ],
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);

$markdown = file_get_contents('https://aiu4.com' . $data['download_url']);
echo $markdown;

Python

import os, requests

headers = {'Authorization': 'Bearer ' + os.environ['D2MD_TOKEN']}

with open('document.pdf', 'rb') as f:
    r = requests.post('https://aiu4.com/api', headers=headers, files={'file': f}, data={'format': 'pdf'})

data = r.json()
markdown = requests.get('https://aiu4.com' + data['download_url']).text
print(markdown)