Validez le flux de travail avant d'utiliser l'API
Commencez dans le poste de travail, confirmez que le résultat vérifié correspond à votre flux de travail, puis utilisez l'API pour des extractions reproductibles.
Base: https://pdf2text.ai/api/v1
Authentification
Toutes les requêtes API nécessitent un jeton Bearer dans l'en-tête Authorization. Générez des clés depuis les paramètres du compte après avoir validé un flux de travail reproductible.
Gérer les clés APIAuthentification
# Set your API key
export P2T_API_KEY="your_api_key_here"
# Include in all requests
curl -H "Authorization: Bearer $P2T_API_KEY" ...
import requests
import os
API_KEY = os.environ["P2T_API_KEY"]
headers = {"Authorization": f"Bearer {API_KEY}"}
const API_KEY = process.env.P2T_API_KEY;
const headers = {
'Authorization': `Bearer ${API_KEY}`
};
Limites d'utilisation
Le plan Firm inclut 1500 pages par mois pour les flux de travail vérifiés dans le poste de travail et les extractions API validées. Chaque page traitée utilise une page de ce quota.
L'utilisation est mesurée en pages traitées, et non en appels API. L'importation de fichiers et le téléchargement de résultats ou d'exports n'utilisent pas de pages, mais relancer une extraction traite à nouveau ces pages et les décompte de nouveau.
Les formats pris en charge pour l'importation sont PDF, PNG, JPEG et WebP, jusqu'à 50 MiB par fichier. Le déploiement actuel limite également la requête multipart complète à 100 MiB, fichiers et surcharge du formulaire compris. Les importations sont limitées à 20 fichiers par requête, 100 fichiers par groupe, 500 pages par document ou exécution et 1 000 pages stockées par groupe. Les images peuvent atteindre 20 000 pixels de côté et 40 mégapixels ; les pages PDF qui dépasseraient ces limites de rastérisation à la résolution de rendu OCR sont rejetées.
Il n'existe pas de quota distinct d'appels API. Les extractions sont asynchrones et entrent dans la file de traitement : leur durée peut donc varier selon la taille des documents et la charge du système.
Les limites de requêtes pour usage équitable sont par défaut de 30 importations, 60 lancements d'exécution et 600 lectures ou exports par compte et par heure. Une réponse 429 inclut Retry-After ; attendez ce délai avant de réessayer.
Utilisez le poste de travail pour valider les mises en page des documents, les règles de vérification et les formats d'export avant de vous appuyer sur l'automatisation par API.
Comparer les plansImporter des documents
Importez un ou plusieurs fichiers PDF, PNG, JPEG ou WebP pour créer un groupe de documents. Les octets des fichiers, le nombre de pages et les dimensions des images sont vérifiés avant tout enregistrement. Une importation réussie renvoie 201 Created avec le page_count vérifié pour chaque document.
Point de terminaison
POST /api/v1/documents/upload/
Paramètres
file / files
binary
PDF, PNG, JPEG ou WebP (obligatoire ; répétez files pour importer plusieurs fichiers)
grouping_id
uuid
Facultatif. Ajouter à l'un de vos groupes API existants. Les espaces de travail anonymes ne peuvent pas être revendiqués par UUID, et une exécution active rend son groupe immuable jusqu'à la fin.
document_type
string
Optionnel. Indice de classification.
password
string
Facultatif. Ouvre les PDF protégés par mot de passe dans cette requête. Il sert uniquement à ouvrir les fichiers et n'est pas conservé. Sans lui, un tel PDF renvoie encrypted_pdf ; un mot de passe erroné renvoie pdf_password_incorrect, et des erreurs répétées pour un même fichier renvoient 429.
Importer des documents
# Upload a PDF file
curl -X POST https://pdf2text.ai/api/v1/documents/upload/ \
-H "Authorization: Bearer $P2T_API_KEY" \
-F "file=@invoice.pdf"
# Add to existing group
curl -X POST https://pdf2text.ai/api/v1/documents/upload/ \
-H "Authorization: Bearer $P2T_API_KEY" \
-F "file=@invoice2.pdf" \
-F "grouping_id=a1b2c3d4-..."
import requests
url = "https://pdf2text.ai/api/v1/documents/upload/"
# Upload single file
with open("invoice.pdf", "rb") as f:
response = requests.post(
url,
headers=headers,
files={"file": f}
)
data = response.json()
group_id = data["group_id"]
const { readFile } = require('node:fs/promises');
const form = new FormData();
form.append(
'file',
new Blob([await readFile('invoice.pdf')], { type: 'application/pdf' }),
'invoice.pdf'
);
const response = await fetch(
'https://pdf2text.ai/api/v1/documents/upload/',
{
method: 'POST',
headers,
body: form
}
);
if (!response.ok) throw new Error(`Upload failed: ${response.status}`);
const { group_id } = await response.json();
{
"status": "success",
"group_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"documents": [
{
"doc_id": 42,
"uid": "1847f600-...",
"grouping_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"original_filename": "invoice.pdf",
"file_type": "pdf",
"page_count": 3,
"status": "pending"
}
]
}
Ajoutez des fichiers à un groupe uniquement avant sa première exécution. Pour les factures suivantes, créez un nouveau groupe ; sinon, une exécution sans filtre traite tous les documents déjà présents dans ce groupe.
Lancer l'extraction
Lancez le traitement asynchrone d'un groupe de documents. Utilisez le routage automatique lorsque PDF2TEXT doit classer les documents avant l'extraction.
Envoyez un en-tête Idempotency-Key avec chaque requête de lancement. Répéter la même clé avec le même corps renvoie la même exécution ; réutiliser la clé pour une requête différente renvoie 409. Après un échec définitif, utilisez une nouvelle clé pour créer volontairement une exécution de remplacement. Une exécution nouvellement mise en file renvoie 202 Accepted. Une seule exécution par groupe peut être active à la fois, et les exécutions actives réservent les pages du compte qu'elles peuvent traiter.
Une réponse 202 confirme que l'exécution a été acceptée, et non que chaque page demandée est couverte. Si le solde partagé du compte est inférieur à l'exécution, l'API traite les pages réservées et se termine en statut partial avec credit_limit_reached=true. Vérifiez le statut final de l'exécution avant de considérer l'import comme terminé.
Les URL de callback doivent utiliser HTTPS public et ne peuvent pas rediriger vers une autre destination. La livraison est asynchrone, avec un délai d'expiration de requête de 10 secondes, et effectue jusqu'à quatre nouvelles tentatives en cas d'erreurs de connexion transitoires, de réponses HTTP 429 et HTTP 5xx, avec des intervalles d'attente de 30 secondes, 2 minutes et 10 minutes.
Lorsque callback_secret est fourni, vérifiez X-PDF2TEXT-Signature en calculant un HMAC-SHA256 sur le corps exact de la requête et en le comparant à la valeur sha256=. Utilisez X-PDF2TEXT-Run-ID ou X-PDF2TEXT-Idempotency-Key pour dédupliquer les livraisons.
The callback JSON body is {"event": "idp.run.completed", "run": {...}}; run uses the same full result shape as GET ?include=results. A signature header is sent only when callback_secret was included in the start request.
Point de terminaison
POST /api/v1/groups/{group_id}/runs/
Corps de la requête
auto_route
boolean
Classification automatique (par défaut : true)
extraction_mode
enum
auto, raw_text, financial_table, general_table ou unified. Utilisez template_id ou template_slug pour un schéma personnalisé.
template_id / template_slug
uuid / string
Utilisez-en exactement un pour imposer un modèle spécifique
selected_pages
array
Liste facultative non vide de {docUid, pageNumber} ; des pages invalides entraînent le rejet de toute la requête
construction_project_id
uuid
Projet facultatif appartenant au compte API
callback_url
string
URL du webhook
callback_secret
string
Clé secrète de signature HMAC
Lancer l'extraction
# Start extraction with auto-routing
curl -X POST https://pdf2text.ai/api/v1/groups/$GROUP_ID/runs/ \
-H "Authorization: Bearer $P2T_API_KEY" \
-H "Idempotency-Key: invoice-import-123" \
-H "Content-Type: application/json" \
-d '{"auto_route": true}'
# Start extraction run
url = f"https://pdf2text.ai/api/v1/groups/{group_id}/runs/"
response = requests.post(
url,
headers={**headers, "Idempotency-Key": "invoice-import-123"},
json={"auto_route": True}
)
run_data = response.json()
run_id = run_data["run_id"]
// Start extraction run
const response = await fetch(
`https://pdf2text.ai/api/v1/groups/${groupId}/runs/`,
{
method: 'POST',
headers: {
...headers,
'Idempotency-Key': 'invoice-import-123',
'Content-Type': 'application/json'
},
body: JSON.stringify({ auto_route: true })
}
);
const { run_id } = await response.json();
{
"status": "success",
"run_id": "b3143b91-...",
"job_id": "idp-run-b3143b91-...",
"group_name": "ocr_job_a1b2c3d4...",
"idempotent_replay": false,
"run": {
"id": "b3143b91-...",
"status": "queued",
"job_id": "idp-run-b3143b91-...",
"total_pages": 3,
"pages_processed": 0
}
}
Récupérer les résultats
Interrogez le point de terminaison d'exécution ou utilisez des webhooks pour récupérer les résultats d'extraction. Exportez en JSON ou Excel.
La réponse d'exécution par défaut est légère pour permettre des interrogations fréquentes et n'est jamais mise en cache. Lisez run.status dans l'objet run imbriqué. Les statuts finaux sont complete, partial et error. Après un statut final, demandez include=results une fois pour récupérer full_text, structured_output et documents.
credits_remaining est conservé pour la compatibilité et enregistre le solde historique après cette exécution. credits_remaining_after_run désigne explicitement la même valeur historique, tandis que account_credits_remaining indique le solde partagé actuel du compte au moment où la réponse est générée.
Points de terminaison
GET /api/v1/runs/{run_id}/
GET /api/v1/runs/{run_id}/?include=results
GET /api/v1/runs/{run_id}/export.json
GET /api/v1/runs/{run_id}/export.xlsx
GET /api/v1/runs/{run_id}/export.xlsx?date_format=MM/DD/YYYY
Pour les relevés bancaires, export.xlsx contient aussi des feuilles CSV QuickBooks et CSV Xero. Leurs dates suivent le format enregistré sur votre compte, ou à défaut l'ordre utilisé dans le pays du relevé. Passez date_format (DD/MM/YYYY, MM/DD/YYYY, YYYY/MM/DD ou YYYY-MM-DD) pour le choisir ; les autres feuilles restent en YYYY-MM-DD.
Codes de statut de réponse
Récupérer les résultats
# Lightweight status poll
curl https://pdf2text.ai/api/v1/runs/$RUN_ID/ \
-H "Authorization: Bearer $P2T_API_KEY"
# Fetch completed results once, then export
curl "https://pdf2text.ai/api/v1/runs/$RUN_ID/?include=results" \
-H "Authorization: Bearer $P2T_API_KEY"
curl -o results.xlsx \
https://pdf2text.ai/api/v1/runs/$RUN_ID/export.xlsx \
-H "Authorization: Bearer $P2T_API_KEY"
import time
run_url = f"https://pdf2text.ai/api/v1/runs/{run_id}/"
terminal = {"complete", "partial", "error"}
delay = 2
# Poll lightweight state with bounded exponential backoff
while True:
response = requests.get(run_url, headers=headers)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", delay))
time.sleep(retry_after)
delay = min(30, max(10, delay * 2))
continue
response.raise_for_status()
run = response.json()["run"]
if run["status"] in terminal:
break
time.sleep(delay)
delay = min(30, max(10, delay * 2))
if run["status"] == "error":
raise RuntimeError(run["error_message"])
results = requests.get(run_url, params={"include": "results"}, headers=headers).json()["run"]
excel = requests.get(
f"{run_url}export.xlsx",
headers=headers
)
with open("results.xlsx", "wb") as f:
f.write(excel.content)
// Poll lightweight state
const pollResults = async (runId) => {
const runUrl = `https://pdf2text.ai/api/v1/runs/${runId}/`;
const terminal = new Set(['complete', 'partial', 'error']);
let delay = 2000;
while (true) {
const response = await fetch(runUrl, { headers });
if (response.status === 429) {
const retryAfter = Number(response.headers.get('Retry-After') || delay / 1000);
await new Promise(r => setTimeout(r, retryAfter * 1000));
delay = Math.min(30000, Math.max(10000, delay * 2));
continue;
}
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const { run } = await response.json();
if (terminal.has(run.status)) {
if (run.status === 'error') throw new Error(run.error_message);
const resultResponse = await fetch(`${runUrl}?include=results`, { headers });
return (await resultResponse.json()).run;
}
await new Promise(r => setTimeout(r, delay));
delay = Math.min(30000, Math.max(10000, delay * 2));
}
};
const results = await pollResults(runId);
{
"status": "success",
"run": {
"id": "b3143b91-...",
"status": "complete",
"total_pages": 3,
"pages_processed": 3,
"credits_remaining_after_run": 497,
"account_credits_remaining": 497,
"full_text": "...",
"structured_output": { /* grouped output */ },
"documents": [
{
"doc_id": 42,
"document_name": "invoice.pdf",
"ocr_text": "...",
"structured_output": { /* document output */ }
}
]
}
}
Modèles
Listez les modèles d'extraction disponibles. Utilisez les identifiants de modèles pour forcer des schémas d'extraction spécifiques.
Point de terminaison
GET /api/v1/templates/
Modèles
# List available templates
curl https://pdf2text.ai/api/v1/templates/ \
-H "Authorization: Bearer $P2T_API_KEY"
# List available templates
response = requests.get(
"https://pdf2text.ai/api/v1/templates/",
headers=headers
)
templates = response.json()["templates"]
for t in templates:
print(f"{t['name']}: {t['id']}")
// List available templates
const response = await fetch(
'https://pdf2text.ai/api/v1/templates/',
{ headers }
);
const { templates } = await response.json();
templates.forEach(t => console.log(`${t.name}: ${t.id}`));
{
"status": "success",
"templates": [
{
"id": "198aa5be-ea8b-4d67-bdb5-625f5d7d29e8",
"slug": "invoice",
"name": "Invoice",
"description": "Standard invoice extraction",
"extraction_mode": "financial_table"
},
{
"id": "4317707a-f0da-4c2f-a1ec-d0119d96b55c",
"slug": "receipt",
"name": "Receipt",
"description": "Receipt and expense extraction",
"extraction_mode": "financial_table"
}
]
}
Besoin d'aide ?
Utilisez la file de vérification pour examiner les champs extraits et conserver le contexte de vérification.
Authentification
# Set your API key
export P2T_API_KEY="your_api_key_here"
# Include in all requests
curl -H "Authorization: Bearer $P2T_API_KEY" ...
import requests
import os
API_KEY = os.environ["P2T_API_KEY"]
headers = {"Authorization": f"Bearer {API_KEY}"}
const API_KEY = process.env.P2T_API_KEY;
const headers = {
'Authorization': `Bearer ${API_KEY}`
};
Importer des documents
# Upload a PDF file
curl -X POST https://pdf2text.ai/api/v1/documents/upload/ \
-H "Authorization: Bearer $P2T_API_KEY" \
-F "file=@invoice.pdf"
# Add to existing group
curl -X POST https://pdf2text.ai/api/v1/documents/upload/ \
-H "Authorization: Bearer $P2T_API_KEY" \
-F "file=@invoice2.pdf" \
-F "grouping_id=a1b2c3d4-..."
import requests
url = "https://pdf2text.ai/api/v1/documents/upload/"
# Upload single file
with open("invoice.pdf", "rb") as f:
response = requests.post(
url,
headers=headers,
files={"file": f}
)
data = response.json()
group_id = data["group_id"]
const { readFile } = require('node:fs/promises');
const form = new FormData();
form.append(
'file',
new Blob([await readFile('invoice.pdf')], { type: 'application/pdf' }),
'invoice.pdf'
);
const response = await fetch(
'https://pdf2text.ai/api/v1/documents/upload/',
{
method: 'POST',
headers,
body: form
}
);
if (!response.ok) throw new Error(`Upload failed: ${response.status}`);
const { group_id } = await response.json();
{
"status": "success",
"group_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"documents": [
{
"doc_id": 42,
"uid": "1847f600-...",
"grouping_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"original_filename": "invoice.pdf",
"file_type": "pdf",
"page_count": 3,
"status": "pending"
}
]
}
Ajoutez des fichiers à un groupe uniquement avant sa première exécution. Pour les factures suivantes, créez un nouveau groupe ; sinon, une exécution sans filtre traite tous les documents déjà présents dans ce groupe.
Lancer l'extraction
# Start extraction with auto-routing
curl -X POST https://pdf2text.ai/api/v1/groups/$GROUP_ID/runs/ \
-H "Authorization: Bearer $P2T_API_KEY" \
-H "Idempotency-Key: invoice-import-123" \
-H "Content-Type: application/json" \
-d '{"auto_route": true}'
# Start extraction run
url = f"https://pdf2text.ai/api/v1/groups/{group_id}/runs/"
response = requests.post(
url,
headers={**headers, "Idempotency-Key": "invoice-import-123"},
json={"auto_route": True}
)
run_data = response.json()
run_id = run_data["run_id"]
// Start extraction run
const response = await fetch(
`https://pdf2text.ai/api/v1/groups/${groupId}/runs/`,
{
method: 'POST',
headers: {
...headers,
'Idempotency-Key': 'invoice-import-123',
'Content-Type': 'application/json'
},
body: JSON.stringify({ auto_route: true })
}
);
const { run_id } = await response.json();
{
"status": "success",
"run_id": "b3143b91-...",
"job_id": "idp-run-b3143b91-...",
"group_name": "ocr_job_a1b2c3d4...",
"idempotent_replay": false,
"run": {
"id": "b3143b91-...",
"status": "queued",
"job_id": "idp-run-b3143b91-...",
"total_pages": 3,
"pages_processed": 0
}
}
Récupérer les résultats
# Lightweight status poll
curl https://pdf2text.ai/api/v1/runs/$RUN_ID/ \
-H "Authorization: Bearer $P2T_API_KEY"
# Fetch completed results once, then export
curl "https://pdf2text.ai/api/v1/runs/$RUN_ID/?include=results" \
-H "Authorization: Bearer $P2T_API_KEY"
curl -o results.xlsx \
https://pdf2text.ai/api/v1/runs/$RUN_ID/export.xlsx \
-H "Authorization: Bearer $P2T_API_KEY"
import time
run_url = f"https://pdf2text.ai/api/v1/runs/{run_id}/"
terminal = {"complete", "partial", "error"}
delay = 2
# Poll lightweight state with bounded exponential backoff
while True:
response = requests.get(run_url, headers=headers)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", delay))
time.sleep(retry_after)
delay = min(30, max(10, delay * 2))
continue
response.raise_for_status()
run = response.json()["run"]
if run["status"] in terminal:
break
time.sleep(delay)
delay = min(30, max(10, delay * 2))
if run["status"] == "error":
raise RuntimeError(run["error_message"])
results = requests.get(run_url, params={"include": "results"}, headers=headers).json()["run"]
excel = requests.get(
f"{run_url}export.xlsx",
headers=headers
)
with open("results.xlsx", "wb") as f:
f.write(excel.content)
// Poll lightweight state
const pollResults = async (runId) => {
const runUrl = `https://pdf2text.ai/api/v1/runs/${runId}/`;
const terminal = new Set(['complete', 'partial', 'error']);
let delay = 2000;
while (true) {
const response = await fetch(runUrl, { headers });
if (response.status === 429) {
const retryAfter = Number(response.headers.get('Retry-After') || delay / 1000);
await new Promise(r => setTimeout(r, retryAfter * 1000));
delay = Math.min(30000, Math.max(10000, delay * 2));
continue;
}
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const { run } = await response.json();
if (terminal.has(run.status)) {
if (run.status === 'error') throw new Error(run.error_message);
const resultResponse = await fetch(`${runUrl}?include=results`, { headers });
return (await resultResponse.json()).run;
}
await new Promise(r => setTimeout(r, delay));
delay = Math.min(30000, Math.max(10000, delay * 2));
}
};
const results = await pollResults(runId);
{
"status": "success",
"run": {
"id": "b3143b91-...",
"status": "complete",
"total_pages": 3,
"pages_processed": 3,
"credits_remaining_after_run": 497,
"account_credits_remaining": 497,
"full_text": "...",
"structured_output": { /* grouped output */ },
"documents": [
{
"doc_id": 42,
"document_name": "invoice.pdf",
"ocr_text": "...",
"structured_output": { /* document output */ }
}
]
}
}
Modèles
# List available templates
curl https://pdf2text.ai/api/v1/templates/ \
-H "Authorization: Bearer $P2T_API_KEY"
# List available templates
response = requests.get(
"https://pdf2text.ai/api/v1/templates/",
headers=headers
)
templates = response.json()["templates"]
for t in templates:
print(f"{t['name']}: {t['id']}")
// List available templates
const response = await fetch(
'https://pdf2text.ai/api/v1/templates/',
{ headers }
);
const { templates } = await response.json();
templates.forEach(t => console.log(`${t.name}: ${t.id}`));
{
"status": "success",
"templates": [
{
"id": "198aa5be-ea8b-4d67-bdb5-625f5d7d29e8",
"slug": "invoice",
"name": "Invoice",
"description": "Standard invoice extraction",
"extraction_mode": "financial_table"
},
{
"id": "4317707a-f0da-4c2f-a1ec-d0119d96b55c",
"slug": "receipt",
"name": "Receipt",
"description": "Receipt and expense extraction",
"extraction_mode": "financial_table"
}
]
}