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
https://aiu4.com/api
Content-Type: multipart/form-data
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
file | file | Yes | The file to convert. Max 200 MB. |
format | string | No | File 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.
| Status | Code | Meaning |
|---|---|---|
| 401 | token_required | No Authorization header, or one that is not a bearer token. |
| 401 | token_invalid | The token does not exist — it was replaced, revoked, or its account was deleted. |
| 403 | plan_required | A 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
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)