Integrate certificate of insurance data extraction directly into your product with a simple, secure API.
API access is part of the Pro plan. Accounts on the Starter and Plus plans can read this page, but calls to the endpoints below are refused.
Authenticate every request with an API token created in your dashboard.
Create a personal API token, then include it in the Authorization header on each call:
Authorization: Bearer YOUR_API_TOKEN
To create an API token:
A new token comes with all four permissions ticked, which is exactly what the endpoints below need. Leave them as they are unless you want a narrower token; unticking one makes the matching call return HTTP 403:
upload-documents for POST /api/documents/uploadextract-data for POST /api/documents/extractview-extractions for GET /api/documents/extraction/{hash}read for GET /api/documents/typesCore endpoints for upload, extraction, and results
/api/documents/upload
Upload a certificate of insurance file to begin processing.
Content-Type: multipart/form-data
{
"document": [file] // Required: The document file to upload
}
{
"success": true,
"message": "File uploaded successfully",
"data": {
"file_id": 1234,
"name": "coi.pdf"
}
}
/api/documents/extract
Start extraction for a previously uploaded file.
Content-Type: application/json
{
"file_id": 1234, // Required: ID of the uploaded file
"document_type_id": 5678 // Optional: ID of the document template to use
}
{
"success": true,
"message": "Extraction initialized successfully",
"data": {
"extraction_id": 9876,
"extraction_hash": "abc123-def456-ghi789-jkl012"
}
}
/api/documents/extraction/{hash}
Fetch the parsed results for a given extraction hash.
{hash}: The extraction hash returned by the extract endpoint
status: pending, processing, completed, error or limit_exceeded. Poll until it is completed; before that the field list can still be empty.message: why the document stopped, when status is error or limit_exceeded (the same sentence the result page shows, for example that the file is not a certificate of insurance or is over the page limit of your plan). It is null for every other status.fields[].label: the template field name, for example Named Insured or Certificate Holder. It is null when the field is not tied to a template.fields[].content: the extracted value itself, as read from the document.fields[].is_table: true for a table field such as the coverage lines.fields[].rows[].cells[]: present only on table fields. Each cell carries value and is_header; the header row comes first and names the columns.Coverage Lines, with the insurance company, NAIC and insurer letter on each policy row, and Insurers Affording Coverage (Insurer Letter, Insurer Name, NAIC #). The ACORD 25 template returns two tables instead: Insurers Affording Coverage (Insurer Letter, Insurer Name, NAIC #) and Coverages, where each policy row names its insurer by letter. Read the column names from the header row rather than relying on their position.
{
"success": true,
"data": {
"extraction_id": 9876,
"extraction_hash": "abc123-def456-ghi789-jkl012",
"file": {
"id": 1234,
"name": "coi.pdf"
},
"document_type": {
"id": 5678,
"name": "Certificate of Insurance (Generic)"
},
"status": "completed",
"message": null,
"fields": [
{
"field_id": 5421,
"label": "Named Insured",
"content": "Wrenfield Steel Erectors LLC\n2290 Foundry Road, Grove City, OH 43123",
"is_table": false,
"page": 1
},
{
"field_id": 5424,
"label": "Certificate Holder",
"content": "Stonegate Industrial Partners LP\n400 Commerce Parkway, Dublin, OH 43017",
"is_table": false,
"page": 1
},
{
"field_id": 5428,
"label": "Certificate Date",
"content": "09/20/2026",
"is_table": false,
"page": 1
},
{
"field_id": 5423,
"label": "Coverage Lines",
"content": "Coverage Lines",
"is_table": true,
"page": 1,
"rows": [
{
"cells": [
{"value": "Coverage Type", "is_header": true},
{"value": "Insurance Company", "is_header": true},
{"value": "NAIC #", "is_header": true},
{"value": "Policy Number", "is_header": true},
{"value": "Effective Date", "is_header": true},
{"value": "Expiration Date", "is_header": true},
{"value": "Limits", "is_header": true},
{"value": "Insurer Letter", "is_header": true}
]
},
{
"cells": [
{"value": "COMMERCIAL GENERAL LIABILITY", "is_header": false},
{"value": "Aldergrove Mutual Casualty Co.", "is_header": false},
{"value": "22357", "is_header": false},
{"value": "GL-UCZ-7714", "is_header": false},
{"value": "10/01/2026", "is_header": false},
{"value": "10/01/2027", "is_header": false},
{"value": "EACH OCCURRENCE $1,000,000 ...", "is_header": false},
{"value": "A", "is_header": false}
]
}
]
},
{
"field_id": 5429,
"label": "Insurers Affording Coverage",
"content": "Insurers Affording Coverage",
"is_table": true,
"page": 1,
"rows": [
{
"cells": [
{"value": "Insurer Letter", "is_header": true},
{"value": "Insurer Name", "is_header": true},
{"value": "NAIC #", "is_header": true}
]
},
{
"cells": [
{"value": "A", "is_header": false},
{"value": "Aldergrove Mutual Casualty Co.", "is_header": false},
{"value": "22357", "is_header": false}
]
}
]
}
]
}
}
/api/documents/types
Retrieve a list of available document templates.
{
"success": true,
"data": [
{
"id": 5678,
"name": "ACORD 25 Certificate",
"description": "Template for processing certificate of insurance documents",
"is_public": true,
"fields_count": 10
},
{
"id": 5679,
"name": "Additional Insured Endorsement",
"description": "Template for processing endorsement documents",
"is_public": false,
"fields_count": 8
}
]
}
Copy‑paste snippets to integrate certificate of insurance data extraction in minutes
# Upload document
curl -X POST https://coisoftware.com/api/documents/upload \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-F "document=@/path/to/document.pdf"
# Extract data
curl -X POST https://coisoftware.com/api/documents/extract \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"file_id": 1234, "document_type_id": 5678}'
# Get results
curl -X GET https://coisoftware.com/api/documents/extraction/abc123-def456-ghi789-jkl012 \
-H "Authorization: Bearer YOUR_API_TOKEN"
<?php
$apiToken = 'YOUR_API_TOKEN';
$apiBaseUrl = 'https://coisoftware.com/api';
// 1. Upload document
$filePath = '/path/to/document.pdf';
$ch = curl_init("$apiBaseUrl/documents/upload");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer $apiToken"
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
'document' => new CURLFile($filePath)
]);
$uploadResponse = curl_exec($ch);
curl_close($ch);
$uploadResult = json_decode($uploadResponse, true);
if (!$uploadResult['success']) {
die('Error uploading document: ' . $uploadResult['message']);
}
$fileId = $uploadResult['data']['file_id'];
echo "File uploaded with ID: $fileId\n";
// 2. Extract data
$ch = curl_init("$apiBaseUrl/documents/extract");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer $apiToken",
"Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'file_id' => $fileId,
'document_type_id' => 5678 // Optional
]));
$extractResponse = curl_exec($ch);
curl_close($ch);
$extractResult = json_decode($extractResponse, true);
if (!$extractResult['success']) {
die('Error extracting data: ' . $extractResult['message']);
}
$extractionHash = $extractResult['data']['extraction_hash'];
echo "Extraction initialized with hash: $extractionHash\n";
// 3. Get extraction results
$ch = curl_init("$apiBaseUrl/documents/extraction/$extractionHash");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer $apiToken"
]);
$resultsResponse = curl_exec($ch);
curl_close($ch);
$resultsData = json_decode($resultsResponse, true);
if (!$resultsData['success']) {
die('Error getting extraction results: ' . $resultsData['message']);
}
// Process the extracted data
$fields = $resultsData['data']['fields'];
foreach ($fields as $field) {
echo ($field['label'] ?? 'Field') . ': ' . $field['content'] . "\n";
}
// Complete document processing with JavaScript
const apiToken = 'YOUR_API_TOKEN';
const apiBaseUrl = 'https://coisoftware.com/api';
// 1. Upload document
async function uploadDocument(filePath) {
const formData = new FormData();
formData.append('document', document.querySelector('input[type="file"]').files[0]);
const response = await fetch(`${apiBaseUrl}/documents/upload`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiToken}`
},
body: formData
});
const result = await response.json();
if (!result.success) {
throw new Error(`Error uploading document: ${result.message}`);
}
console.log(`File uploaded with ID: ${result.data.file_id}`);
return result.data.file_id;
}
// 2. Extract data
async function extractData(fileId, documentTypeId = null) {
const payload = {
file_id: fileId
};
if (documentTypeId) {
payload.document_type_id = documentTypeId;
}
const response = await fetch(`${apiBaseUrl}/documents/extract`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
const result = await response.json();
if (!result.success) {
throw new Error(`Error extracting data: ${result.message}`);
}
console.log(`Extraction initialized with hash: ${result.data.extraction_hash}`);
return result.data.extraction_hash;
}
// 3. Get extraction results
async function getExtractionResults(extractionHash) {
const response = await fetch(`${apiBaseUrl}/documents/extraction/${extractionHash}`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${apiToken}`
}
});
const result = await response.json();
if (!result.success) {
throw new Error(`Error getting extraction results: ${result.message}`);
}
console.log('Extraction results:', result.data);
// Process the extracted data
const fields = result.data.fields;
fields.forEach(field => {
console.log(`${field.label ?? 'Field'}: ${field.content}`);
});
return result.data;
}
// Usage
async function processDocument() {
try {
const fileId = await uploadDocument();
const extractionHash = await extractData(fileId, 5678); // Optional document type ID
const extractionData = await getExtractionResults(extractionHash);
// Do something with the extracted data
displayResults(extractionData);
} catch (error) {
console.error(error);
}
}
function displayResults(data) {
// Display the results in your application
const resultsDiv = document.getElementById('results');
resultsDiv.innerHTML = '';
data.fields.forEach(field => {
const fieldEl = document.createElement('div');
fieldEl.classList.add('field');
fieldEl.innerHTML = `${field.label ?? 'Field'}: ${field.content}`;
resultsDiv.appendChild(fieldEl);
});
}
import requests
import json
import time
api_token = 'YOUR_API_TOKEN'
api_base_url = 'https://coisoftware.com/api'
# 1. Upload document
def upload_document(file_path):
headers = {
'Authorization': f'Bearer {api_token}'
}
with open(file_path, 'rb') as file:
files = {'document': file}
response = requests.post(
f'{api_base_url}/documents/upload',
headers=headers,
files=files
)
result = response.json()
if not result.get('success'):
raise Exception(f"Error uploading document: {result.get('message')}")
file_id = result['data']['file_id']
print(f"File uploaded with ID: {file_id}")
return file_id
# 2. Extract data
def extract_data(file_id, document_type_id=None):
headers = {
'Authorization': f'Bearer {api_token}',
'Content-Type': 'application/json'
}
payload = {
'file_id': file_id
}
if document_type_id:
payload['document_type_id'] = document_type_id
response = requests.post(
f'{api_base_url}/documents/extract',
headers=headers,
data=json.dumps(payload)
)
result = response.json()
if not result.get('success'):
raise Exception(f"Error extracting data: {result.get('message')}")
extraction_hash = result['data']['extraction_hash']
print(f"Extraction initialized with hash: {extraction_hash}")
return extraction_hash
# 3. Get extraction results
def get_extraction_results(extraction_hash):
headers = {
'Authorization': f'Bearer {api_token}'
}
response = requests.get(
f'{api_base_url}/documents/extraction/{extraction_hash}',
headers=headers
)
result = response.json()
if not result.get('success'):
raise Exception(f"Error getting extraction results: {result.get('message')}")
print("Extraction results:")
fields = result['data']['fields']
for field in fields:
print(f"{field.get('label') or 'Field'}: {field['content']}")
return result['data']
# Usage
def process_document(file_path, document_type_id=None):
try:
# Upload document
file_id = upload_document(file_path)
# Extract data
extraction_hash = extract_data(file_id, document_type_id)
# Get results (you might need to wait for processing to complete)
time.sleep(2) # Simple delay to wait for processing
extraction_data = get_extraction_results(extraction_hash)
# Process the extracted data further if needed
return extraction_data
except Exception as e:
print(f"Error: {str(e)}")
return None
# Process certificate of insurance
if __name__ == "__main__":
result = process_document('/path/to/coi.pdf', 5678) # Optional document type ID
print(json.dumps(result, indent=2))
What actually limits your usage, and how to get help
There is no per-hour request limit on the API today, and no HTTP 429 response. What does limit you is the monthly certificate allowance of your plan, the same pool the web application draws on: once it is used up, an extraction is refused until the allowance resets or you move up a plan.
Please still call the API at a sensible pace. A tight polling loop on GET /api/documents/extraction/{hash} gains nothing: poll every few seconds and stop as soon as status is completed.
If you have any questions or need assistance with our API, please contact us:
No credit card required. New accounts start with a one-time demo of 10 documents.
Start tracking vendor certificates of insurance and never miss an expiration.
USD
per month
per month
$288 charged today
One shared monthly allowance. Spend it on either model, or mix: 1 Pro AI certificate = 5 Base AI certificates. A certificate counts as one, whatever its page count.
| Base AI Faster | 2,500 certificates |
| Pro AI Best accuracy | 500 certificates |
Four times the monthly certificate allowance and four times the Smart Reports.
USD
per month
per month
$888 charged today
One shared monthly allowance. Spend it on either model, or mix: 1 Pro AI certificate = 5 Base AI certificates. A certificate counts as one, whatever its page count.
| Base AI Faster | 10,000 certificates |
| Pro AI Best accuracy | 2,000 certificates |
The largest certificate allowances, API access and the most Smart Reports.
USD
per month
per month
$ charged today
Two separate monthly allowances, one per AI model. Certificates read with Base AI count only toward the Base AI allowance, and certificates read with Pro AI only toward the Pro AI allowance. A certificate counts as one, whatever its page count.
| Base AI Faster | certificates |
| Pro AI Best accuracy | certificates |
Double the Pro 350K allowances for the largest certificate programs.
USD
per month
$59,880 charged today