추출 실행
개요
추출 엔드포인트는 문서(base64 인코딩)를 받아 사용자가 정의한 템플릿을 통해 처리하고, 템플릿의 변수와 일치하는 구조화된 데이터를 반환합니다. 단일 API 호출로 업로드, AI 처리, 구조화된 출력까지 전체 파이프라인을 처리합니다.
모든 추출 요청은 POST /v1/extractions/run을 통해 이루어집니다.
문서 준비
문서를 API로 전송하기 전에 파일 내용을 base64로 인코딩해야 합니다.
제약 조건:
- 최대 요청 본문 크기: 15 MB
- 지원되는 MIME 타입:
application/pdf(PDF)application/vnd.openxmlformats-officedocument.wordprocessingml.document(DOCX)
# Base64-encode a PDF file
base64 -i invoice.pdf -o invoice_b64.txt
# Or inline (macOS / Linux)
PDF_BASE64=$(base64 -w 0 invoice.pdf)import { readFileSync } from "fs";
const pdfBuffer = readFileSync("invoice.pdf");
const pdfBase64 = pdfBuffer.toString("base64");import base64
with open("invoice.pdf", "rb") as f:
pdf_base64 = base64.b64encode(f.read()).decode("utf-8")WARNING
Base64 인코딩은 파일 크기를 약 33% 증가시킵니다. 10 MB PDF는 인코딩 후 약 13.3 MB가 되므로, 15 MB 요청 제한을 준수하려면 원본 파일을 ~11 MB 이하로 유지하세요.
요청 보내기
다음 JSON 본문으로 /v1/extractions/run에 POST 요청을 전송합니다:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
templateId | string | 예 | 추출에 사용할 템플릿의 ID |
fileName | string | 예 | 원본 파일명 (예: "invoice.pdf") |
pdfBase64 | string | 예 | Base64 인코딩된 파일 내용 |
mimeType | string | 예 | 파일의 MIME 타입 |
runId | string | 아니오 | 여러 추출을 배치로 그룹화하기 위한 선택적 식별자 |
curl -X POST https://api.docmap.io/v1/extractions/run \
-H "Authorization: Bearer dm_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"templateId": "tmpl_abc123",
"fileName": "invoice.pdf",
"pdfBase64": "JVBERi0xLjQKJeLj...",
"mimeType": "application/pdf"
}'const response = await fetch("https://api.docmap.io/v1/extractions/run", {
method: "POST",
headers: {
Authorization: "Bearer dm_live_your_api_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
templateId: "tmpl_abc123",
fileName: "invoice.pdf",
pdfBase64: pdfBase64,
mimeType: "application/pdf",
}),
});
const { data } = await response.json();
console.log(data.extractedData);import requests
response = requests.post(
"https://api.docmap.io/v1/extractions/run",
headers={
"Authorization": "Bearer dm_live_your_api_key",
"Content-Type": "application/json",
},
json={
"templateId": "tmpl_abc123",
"fileName": "invoice.pdf",
"pdfBase64": pdf_base64,
"mimeType": "application/pdf",
},
)
data = response.json()["data"]
print(data["extractedData"])응답 이해하기
성공적인 추출은 data 객체로 감싸진 응답을 반환합니다:
{
"data": {
"id": "ext_abc123def456",
"userId": "user_789",
"templateId": "tmpl_abc123",
"templateName": "Invoice Parser",
"fileName": "invoice.pdf",
"status": "completed",
"extractedData": {
"vendor_name": "Acme Corp",
"invoice_number": "INV-2024-001",
"total_amount": 1250.00,
"line_items": [
{ "description": "Widget A", "quantity": 10, "unit_price": 125.00 }
]
},
"error": null,
"variables": [
{ "name": "vendor_name", "type": "string", "description": "Company name of the vendor" },
{ "name": "invoice_number", "type": "string", "description": "Invoice reference number" },
{ "name": "total_amount", "type": "number", "description": "Total invoice amount" },
{ "name": "line_items", "type": "array", "description": "List of line items" }
],
"source": "api",
"runId": null,
"processingTimeMs": 3420,
"createdAt": "2025-07-15T10:30:00.000Z"
}
}| 필드 | 설명 |
|---|---|
id | 고유 추출 ID (ext_ 접두사) |
status | 실행 중이면 (비동기 모드) "processing", 데이터가 성공적으로 추출되면 "completed", 처리 중 오류가 발생하면 "failed" |
extractedData | 키가 템플릿의 변수 이름과 일치하는 객체. 추출 실패 시 null |
error | 추출 실패 시 오류 메시지 문자열. 성공 시 null |
processingTimeMs | AI가 문서를 처리하는 데 걸린 시간(밀리초) |
source | API 키를 통해 트리거된 경우 "api", 웹 UI에서 트리거된 경우 "dashboard" |
variables | 이 추출에 사용된 템플릿 변수 정의 |
runId | 제공한 배치 식별자, 또는 지정되지 않은 경우 null |
templateName | 사용된 템플릿의 사람이 읽을 수 있는 이름 |
createdAt | 추출이 생성된 ISO 8601 타임스탬프 |
TIP
extractedData에 접근하기 전에 항상 status 필드를 확인하세요. status가 "failed"인 경우 error 필드에 무엇이 잘못되었는지에 대한 설명이 포함됩니다.
배치 추출
여러 파일을 논리적 배치로 처리하려면 각 추출 요청에 동일한 runId를 전달합니다. 이는 문서 처리 방식을 변경하지 않으며 -- 각 파일은 여전히 독립적으로 추출됩니다 -- 배치의 모든 결과를 함께 조회할 수 있습니다.
const runId = "batch-invoices-2025-07";
const files = ["invoice-001.pdf", "invoice-002.pdf", "invoice-003.pdf"];
// Process each file with the same runId
const results = await Promise.all(
files.map(async (fileName) => {
const pdfBase64 = readFileSync(fileName).toString("base64");
const response = await fetch("https://api.docmap.io/v1/extractions/run", {
method: "POST",
headers: {
Authorization: "Bearer dm_live_your_api_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
templateId: "tmpl_abc123",
fileName,
pdfBase64,
mimeType: "application/pdf",
runId,
}),
});
return response.json();
})
);그런 다음 배치의 모든 추출을 조회합니다:
curl "https://api.docmap.io/v1/extractions?runId=batch-invoices-2025-07" \
-H "Authorization: Bearer dm_live_your_api_key"const response = await fetch(
"https://api.docmap.io/v1/extractions?runId=batch-invoices-2025-07",
{
headers: { Authorization: "Bearer dm_live_your_api_key" },
}
);
const { data } = await response.json();
console.log(`Batch contains ${data.length} extractions`);response = requests.get(
"https://api.docmap.io/v1/extractions",
params={"runId": "batch-invoices-2025-07"},
headers={"Authorization": "Bearer dm_live_your_api_key"},
)
data = response.json()["data"]
print(f"Batch contains {len(data)} extractions")TIP
목록 엔드포인트는 templateId 필터링과 limit 파라미터 (1--100, 기본값 50)도 지원합니다. 필터를 조합할 수 있습니다: ?runId=batch-001&templateId=tmpl_abc123&limit=100.
비동기 워크플로
기본적으로 추출 요청은 동기 방식입니다 -- API는 처리가 완료될 때까지 차단됩니다. 장시간 실행되는 추출이나 HTTP 타임아웃을 피하고 싶을 때는 URL에 ?async=true를 추가하여 비동기 모드를 사용하세요. API는 "processing" 상태로 즉시 반환하며, 결과가 준비될 때까지 별도의 엔드포인트를 폴링합니다.
제출 + 폴링 패턴
import { readFileSync } from "fs";
const API_BASE = "https://api.docmap.io";
const API_KEY = process.env.DOCMAP_API_KEY!;
// 1. Submit the extraction asynchronously
const submitResponse = await fetch(`${API_BASE}/v1/extractions/run?async=true`, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
templateId: "tmpl_abc123",
fileName: "invoice.pdf",
pdfBase64: readFileSync("invoice.pdf").toString("base64"),
mimeType: "application/pdf",
}),
});
const { data: submitted } = await submitResponse.json();
console.log(`Extraction ${submitted.id} submitted, status: ${submitted.status}`);
// 2. Poll until complete
async function poll(extractionId: string): Promise<any> {
for (let i = 0; i < 30; i++) {
const res = await fetch(`${API_BASE}/v1/extractions/${extractionId}`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
const { data } = await res.json();
if (data.status !== "processing") return data;
await new Promise((r) => setTimeout(r, 2000));
}
throw new Error("Extraction timed out");
}
const result = await poll(submitted.id);
console.log(`Final status: ${result.status}`);
console.log("Extracted data:", result.extractedData);import base64
import time
import requests
API_BASE = "https://api.docmap.io"
API_KEY = "dm_live_your_api_key"
headers = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
# 1. Submit the extraction asynchronously
with open("invoice.pdf", "rb") as f:
pdf_base64 = base64.b64encode(f.read()).decode("utf-8")
submit_response = requests.post(
f"{API_BASE}/v1/extractions/run?async=true",
headers=headers,
json={
"templateId": "tmpl_abc123",
"fileName": "invoice.pdf",
"pdfBase64": pdf_base64,
"mimeType": "application/pdf",
},
)
submitted = submit_response.json()["data"]
print(f"Extraction {submitted['id']} submitted, status: {submitted['status']}")
# 2. Poll until complete
def poll(extraction_id: str):
for _ in range(30):
res = requests.get(
f"{API_BASE}/v1/extractions/{extraction_id}",
headers={"Authorization": f"Bearer {API_KEY}"},
)
data = res.json()["data"]
if data["status"] != "processing":
return data
time.sleep(2)
raise TimeoutError("Extraction timed out")
result = poll(submitted["id"])
print(f"Final status: {result['status']}")
print("Extracted data:", result["extractedData"])비동기 모드를 사용해야 할 때
대용량 문서를 처리할 때, HTTP 클라이언트의 타임아웃이 짧을 때, 또는 여러 추출을 제출하고 나중에 결과를 수집하려는 경우 비동기 모드를 사용하세요. 대부분의 단일 문서 추출의 경우 동기 모드가 더 간단합니다.
전체 워크플로 예제
다음은 디스크에서 PDF를 읽고, 추출을 실행하고, 성공과 실패를 모두 처리하는 TypeScript의 전체 엔드투엔드 예제입니다:
import { readFileSync } from "fs";
const API_BASE = "https://api.docmap.io";
const API_KEY = process.env.DOCMAP_API_KEY!;
async function extractInvoice(filePath: string) {
// 1. Read and encode the PDF
const pdfBuffer = readFileSync(filePath);
const pdfBase64 = pdfBuffer.toString("base64");
const fileName = filePath.split("/").pop()!;
console.log(`Processing ${fileName} (${(pdfBuffer.length / 1024).toFixed(0)} KB)...`);
// 2. Run the extraction
const response = await fetch(`${API_BASE}/v1/extractions/run`, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
templateId: "tmpl_invoice_parser",
fileName,
pdfBase64,
mimeType: "application/pdf",
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(`API error ${response.status}: ${error.error.message}`);
}
const { data } = await response.json();
// 3. Check the extraction result
if (data.status === "failed") {
console.error(`Extraction failed: ${data.error}`);
return null;
}
console.log(`Extraction completed in ${data.processingTimeMs}ms`);
console.log("Extracted data:", JSON.stringify(data.extractedData, null, 2));
return data.extractedData;
}
// Run it
extractInvoice("./invoices/invoice-001.pdf")
.then((result) => {
if (result) {
console.log(`Vendor: ${result.vendor_name}`);
console.log(`Total: $${result.total_amount}`);
}
})
.catch(console.error);