Need API access or technical support?Contact us
The ODOV Tax Forms API provides programmatic access to tax form due dates and calculation rules. This documentation covers both API versions currently supported.
Base URL: https://tax-forms.odov.io/api
All API requests must include your API key in the request header:
X-API-Key: your-api-key-hereRate Limiting: API keys are limited to 100 requests per 10 minutes. Contact support if you need higher limits.
/api/v2/calculateCalculate due dates for a specific tax form
{
"formNumber": "1040",
"entityType": "individual",
"locality": "United States",
"localityType": "federal",
"coverageStartDate": "2024-01-01",
"coverageEndDate": "2024-12-31"
}{
"formNumber": "1040",
"formName": "U.S. Individual Income Tax Return",
"calculationBase": "end",
"dates": {
"dueDate": "2025-04-15T23:00:00.000Z",
"extensionDueDate": "2025-10-15T23:00:00.000Z"
},
"requestedExtensionLevel": "1",
"extensions": {
"1": {
"formNumber": "4868",
"formName": "Application for Automatic Extension of Time to File U.S. Individual Income Tax Return",
"extensionType": "automatic",
"filingRequired": true,
"paymentRequired": true,
"piggybackedBy": [
{
"formNumber": "IT-303",
"locality": "Georgia"
}
]
}
}
}curl -X POST https://tax-forms.odov.io/api/v2/calculate \
-H "X-API-Key: your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"formNumber": "1040",
"entityType": "individual",
"locality": "United States",
"localityType": "federal",
"coverageStartDate": "2024-01-01",
"coverageEndDate": "2024-12-31"
}'const response = await fetch('https://tax-forms.odov.io/api/v2/calculate', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key-here',
'Content-Type': 'application/json'
},
body: JSON.stringify({
formNumber: '1040',
entityType: 'individual',
locality: 'United States',
localityType: 'federal',
coverageStartDate: '2024-01-01',
coverageEndDate: '2024-12-31'
})
});
const data = await response.json();
console.log(data);import requests
response = requests.post(
'https://tax-forms.odov.io/api/v2/calculate',
headers={
'X-API-Key': 'your-api-key-here',
'Content-Type': 'application/json'
},
json={
'formNumber': '1040',
'entityType': 'individual',
'locality': 'United States',
'localityType': 'federal',
'coverageStartDate': '2024-01-01',
'coverageEndDate': '2024-12-31'
}
)
data = response.json()
print(data)require 'net/http'
require 'json'
uri = URI('https://tax-forms.odov.io/api/v2/calculate')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['X-API-Key'] = 'your-api-key-here'
request['Content-Type'] = 'application/json'
request.body = {
formNumber: '1040',
entityType: 'individual',
locality: 'United States',
localityType: 'federal',
coverageStartDate: '2024-01-01',
coverageEndDate: '2024-12-31'
}.to_json
response = http.request(request)
data = JSON.parse(response.body)
puts data/api/v2/formsList all available forms with their calculation rules
| Parameter | Type | Description |
|---|---|---|
entityType | string | Filter by entity type (individual, corporation, etc.) |
localityType | string | Filter by locality type (federal, state, city) |
locality | string | Filter by specific locality (e.g., "California") |
since | string | ISO 8601 date - Get only forms added/modified after this date |
{
"forms": [
{
"formNumber": "1040",
"formName": "U.S. Individual Income Tax Return",
"entityType": "individual",
"localityType": "federal",
"locality": "Federal",
"parentFormNumbers": [],
"calculationRules": [
{
"years": [2024, 2025],
"dueDate": {
"type": "fixed",
"monthsAfterPeriodEnd": 3,
"dayOfMonth": 15
},
"extensions": {
"available": true,
"automatic": true,
"automaticMonths": 6
}
}
]
}
],
"metadata": {
"version": "2.0",
"lastUpdated": "2024-01-15T10:30:00Z",
"totalForms": 150
}
}curl -X GET "https://tax-forms.odov.io/api/v2/forms?entityType=individual&localityType=federal" \
-H "X-API-Key: your-api-key-here"const response = await fetch('https://tax-forms.odov.io/api/v2/forms?entityType=individual&localityType=federal', {
headers: {
'X-API-Key': 'your-api-key-here'
}
});
const data = await response.json();
console.log(data);import requests
response = requests.get(
'https://tax-forms.odov.io/api/v2/forms',
headers={'X-API-Key': 'your-api-key-here'},
params={
'entityType': 'individual',
'localityType': 'federal'
}
)
data = response.json()
print(data)require 'net/http'
require 'json'
uri = URI('https://tax-forms.odov.io/api/v2/forms')
uri.query = URI.encode_www_form({
entityType: 'individual',
localityType: 'federal'
})
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['X-API-Key'] = 'your-api-key-here'
response = http.request(request)
data = JSON.parse(response.body)
puts data/api/v2/forms?since={ISO-8601-date}Track forms that have been added or modified after a specific date
This endpoint is perfect for keeping your local database in sync. Pass an ISO 8601 date to get only forms that have been added or modified since that date.
| Parameter | Required | Description |
|---|---|---|
since | Yes | ISO 8601 date (e.g., 2024-01-01T00:00:00Z) |
entityType | No | Filter by entity type |
localityType | No | Filter by locality type |
{
"forms": [
{
"formNumber": "941",
"formName": "Employer's Quarterly Federal Tax Return",
"localityType": "federal",
"locality": "United States",
"entityType": "business",
"lastModified": "2024-03-15T14:30:00Z",
// ... other form fields
}
],
"metadata": {
"version": "2.0",
"lastUpdated": "2024-03-15T14:30:00Z"
},
"changesCount": 1,
"since": "2024-01-01T00:00:00Z"
}curl -X GET "https://tax-forms.odov.io/api/v2/forms?since=2024-01-01T00:00:00Z" \
-H "X-API-Key: your-api-key-here"// Get forms modified in the last 7 days
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
const response = await fetch(
`https://tax-forms.odov.io/api/v2/forms?since=${sevenDaysAgo.toISOString()}`,
{
headers: {
'X-API-Key': 'your-api-key-here'
}
}
);
const data = await response.json();
console.log(`Found ${data.changesCount} changed forms`);import requests
from datetime import datetime, timedelta
# Get forms modified in the last 7 days
seven_days_ago = (datetime.now() - timedelta(days=7)).isoformat() + 'Z'
response = requests.get(
'https://tax-forms.odov.io/api/v2/forms',
headers={'X-API-Key': 'your-api-key-here'},
params={'since': seven_days_ago}
)
data = response.json()
print(f"Found {data['changesCount']} changed forms")require 'net/http'
require 'json'
require 'time'
# Get forms modified in the last 7 days
seven_days_ago = (Time.now - 7 * 24 * 60 * 60).iso8601
uri = URI('https://tax-forms.odov.io/api/v2/forms')
uri.query = URI.encode_www_form({since: seven_days_ago})
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['X-API-Key'] = 'your-api-key-here'
response = http.request(request)
data = JSON.parse(response.body)
puts "Found #{data['changesCount']} changed forms"lastUpdated timestamp from each responsesince parameter in your next request/api/v2/forms/{formNumber}Get detailed information about a specific form
| Parameter | Required | Description |
|---|---|---|
entityType | Sometimes | Required if form exists for multiple entity types |
locality | Sometimes | Required for state/local forms |
curl -X GET "https://tax-forms.odov.io/api/v2/forms/1040?entityType=individual" \
-H "X-API-Key: your-api-key-here"const response = await fetch('https://tax-forms.odov.io/api/v2/forms/1040?entityType=individual', {
headers: {
'X-API-Key': 'your-api-key-here'
}
});
const data = await response.json();
console.log(data);import requests
response = requests.get(
'https://tax-forms.odov.io/api/v2/forms/1040',
headers={'X-API-Key': 'your-api-key-here'},
params={'entityType': 'individual'}
)
data = response.json()
print(data)require 'net/http'
require 'json'
uri = URI('https://tax-forms.odov.io/api/v2/forms/1040')
uri.query = URI.encode_www_form({entityType: 'individual'})
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['X-API-Key'] = 'your-api-key-here'
response = http.request(request)
data = JSON.parse(response.body)
puts data/api/v2/calculate-simpleNew!Calculate due dates with just a form number - returns all variations
Simplified calculation endpoint that only requires a form number. If the form exists for multiple entity types or localities, it returns calculations for all variations.
{
"formNumber": "1040",
"coverageStartDate": "2024-01-01",
"coverageEndDate": "2024-12-31"
}{
"formNumber": "1040",
"formName": "U.S. Individual Income Tax Return",
"entityType": "individual",
"locality": "United States",
"localityType": "federal",
"calculationBase": "end",
"dates": {
"dueDate": "2025-04-15T23:00:00.000Z",
"extensionDueDate": "2025-10-15T23:00:00.000Z"
},
"extensions": {
"1": {
"formNumber": "4868",
"formName": "Application for Automatic Extension...",
"extensionType": "automatic",
"filingRequired": true,
"paymentRequired": true
}
}
}{
"formNumber": "05-158",
"variationCount": 2,
"results": [
{
"formNumber": "05-158",
"formName": "Texas Franchise Tax Report - Short Form",
"entityType": "corporation",
"locality": "Texas",
"localityType": "state",
// ... dates and extensions
},
{
"formNumber": "05-158",
"formName": "Texas Franchise Tax Report - Short Form",
"entityType": "scorp",
"locality": "Texas",
"localityType": "state",
// ... dates and extensions
}
],
"message": "Multiple variations found. Each has different due dates based on entity type and locality."
}curl -X POST https://tax-forms.odov.io/api/v2/calculate-simple \
-H "X-API-Key: your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"formNumber": "1040",
"coverageStartDate": "2024-01-01",
"coverageEndDate": "2024-12-31"
}'const response = await fetch('https://tax-forms.odov.io/api/v2/calculate-simple', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key-here',
'Content-Type': 'application/json'
},
body: JSON.stringify({
formNumber: '1040',
coverageStartDate: '2024-01-01',
coverageEndDate: '2024-12-31'
})
});
const data = await response.json();
console.log(data);import requests
response = requests.post(
'https://tax-forms.odov.io/api/v2/calculate-simple',
headers={
'X-API-Key': 'your-api-key-here',
'Content-Type': 'application/json'
},
json={
'formNumber': '1040',
'coverageStartDate': '2024-01-01',
'coverageEndDate': '2024-12-31'
}
)
data = response.json()
print(data)require 'net/http'
require 'json'
uri = URI('https://tax-forms.odov.io/api/v2/calculate-simple')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['X-API-Key'] = 'your-api-key-here'
request['Content-Type'] = 'application/json'
request.body = {
formNumber: '1040',
coverageStartDate: '2024-01-01',
coverageEndDate: '2024-12-31'
}.to_json
response = http.request(request)
data = JSON.parse(response.body)
puts dataUse these endpoints to discover available values for forms, entity types, and localities.
/api/v2/metadata/form-numbersGet all available form numbers with their names and variation counts
{
"formNumbers": [
{
"formNumber": "1040",
"formName": "U.S. Individual Income Tax Return",
"variations": 1
},
{
"formNumber": "05-158",
"formName": "Texas Franchise Tax Report - Short Form",
"variations": 2
}
],
"count": 29
}curl -X GET "https://tax-forms.odov.io/api/v2/metadata/form-numbers" \
-H "X-API-Key: your-api-key-here"const response = await fetch('https://tax-forms.odov.io/api/v2/metadata/form-numbers', {
headers: {
'X-API-Key': 'your-api-key-here'
}
});
const data = await response.json();
console.log(data);import requests
response = requests.get(
'https://tax-forms.odov.io/api/v2/metadata/form-numbers',
headers={'X-API-Key': 'your-api-key-here'}
)
data = response.json()
print(data)require 'net/http'
require 'json'
uri = URI('https://tax-forms.odov.io/api/v2/metadata/form-numbers')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['X-API-Key'] = 'your-api-key-here'
response = http.request(request)
data = JSON.parse(response.body)
puts data/api/v2/metadata/entity-typesGet all entity types with display names and form counts
{
"entityTypes": [
{
"value": "individual",
"displayName": "Individual",
"formCount": 6
},
{
"value": "corporation",
"displayName": "Corporation",
"formCount": 7
}
],
"count": 5
}curl -X GET "https://tax-forms.odov.io/api/v2/metadata/entity-types" \
-H "X-API-Key: your-api-key-here"const response = await fetch('https://tax-forms.odov.io/api/v2/metadata/entity-types', {
headers: {
'X-API-Key': 'your-api-key-here'
}
});
const data = await response.json();
console.log(data);import requests
response = requests.get(
'https://tax-forms.odov.io/api/v2/metadata/entity-types',
headers={'X-API-Key': 'your-api-key-here'}
)
data = response.json()
print(data)require 'net/http'
require 'json'
uri = URI('https://tax-forms.odov.io/api/v2/metadata/entity-types')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['X-API-Key'] = 'your-api-key-here'
response = http.request(request)
data = JSON.parse(response.body)
puts data/api/v2/metadata/localitiesGet all localities with their types and form counts
| Parameter | Type | Description |
|---|---|---|
localityType | string | Filter by locality type (federal, state, city) |
{
"localities": [
{
"locality": "United States",
"localityType": "federal",
"formCount": 15
},
{
"locality": "California",
"localityType": "state",
"formCount": 8
}
],
"count": 10
}curl -X GET "https://tax-forms.odov.io/api/v2/metadata/localities?localityType=state" \
-H "X-API-Key: your-api-key-here"const response = await fetch('https://tax-forms.odov.io/api/v2/metadata/localities?localityType=state', {
headers: {
'X-API-Key': 'your-api-key-here'
}
});
const data = await response.json();
console.log(data);import requests
response = requests.get(
'https://tax-forms.odov.io/api/v2/metadata/localities',
headers={'X-API-Key': 'your-api-key-here'},
params={'localityType': 'state'}
)
data = response.json()
print(data)require 'net/http'
require 'json'
uri = URI('https://tax-forms.odov.io/api/v2/metadata/localities')
uri.query = URI.encode_www_form({localityType: 'state'})
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['X-API-Key'] = 'your-api-key-here'
response = http.request(request)
data = JSON.parse(response.body)
puts data/api/v2/metadata/locality-typesGet all locality types with display names and form counts
{
"localityTypes": [
{
"value": "federal",
"displayName": "Federal",
"formCount": 15
},
{
"value": "state",
"displayName": "State",
"formCount": 12
},
{
"value": "city",
"displayName": "City/Local",
"formCount": 2
}
],
"count": 3
}curl -X GET "https://tax-forms.odov.io/api/v2/metadata/locality-types" \
-H "X-API-Key: your-api-key-here"const response = await fetch('https://tax-forms.odov.io/api/v2/metadata/locality-types', {
headers: {
'X-API-Key': 'your-api-key-here'
}
});
const data = await response.json();
console.log(data);import requests
response = requests.get(
'https://tax-forms.odov.io/api/v2/metadata/locality-types',
headers={'X-API-Key': 'your-api-key-here'}
)
data = response.json()
print(data)require 'net/http'
require 'json'
uri = URI('https://tax-forms.odov.io/api/v2/metadata/locality-types')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['X-API-Key'] = 'your-api-key-here'
response = http.request(request)
data = JSON.parse(response.body)
puts dataNote: Version 1 is maintained for backwards compatibility. New integrations should use Version 2 for access to historical data and year-specific rules.
/api/v1/calculateCalculate due dates (returns single date, no historical data)
{
"formNumber": "1040",
"entityType": "individual",
"localityType": "federal",
"locality": "United States",
"coverageStartDate": "2024-01-01",
"coverageEndDate": "2024-12-31"
}{
"formNumber": "1040",
"formName": "U.S. Individual Income Tax Return",
"calculationBase": "end",
"dates": {
"dueDate": "2025-04-15T23:00:00.000Z",
"extensionDueDate": "2025-10-15T23:00:00.000Z"
},
"extension": {
"formNumber": "4868",
"formName": "Application for Automatic Extension of Time to File U.S. Individual Income Tax Return",
"piggybackFed": false
}
}curl -X POST https://tax-forms.odov.io/api/v1/calculate \
-H "X-API-Key: your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"formNumber": "1040",
"entityType": "individual",
"localityType": "federal",
"locality": "United States",
"coverageStartDate": "2024-01-01",
"coverageEndDate": "2024-12-31"
}'const response = await fetch('https://tax-forms.odov.io/api/v1/calculate', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key-here',
'Content-Type': 'application/json'
},
body: JSON.stringify({
formNumber: '1040',
entityType: 'individual',
localityType: 'federal',
locality: 'United States',
coverageStartDate: '2024-01-01',
coverageEndDate: '2024-12-31'
})
});
const data = await response.json();
console.log(data);import requests
response = requests.post(
'https://tax-forms.odov.io/api/v1/calculate',
headers={
'X-API-Key': 'your-api-key-here',
'Content-Type': 'application/json'
},
json={
'formNumber': '1040',
'entityType': 'individual',
'localityType': 'federal',
'locality': 'United States',
'coverageStartDate': '2024-01-01',
'coverageEndDate': '2024-12-31'
}
)
data = response.json()
print(data)require 'net/http'
require 'json'
uri = URI('https://tax-forms.odov.io/api/v1/calculate')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['X-API-Key'] = 'your-api-key-here'
request['Content-Type'] = 'application/json'
request.body = {
formNumber: '1040',
entityType: 'individual',
localityType: 'federal',
locality: 'United States',
coverageStartDate: '2024-01-01',
coverageEndDate: '2024-12-31'
}.to_json
response = http.request(request)
data = JSON.parse(response.body)
puts data/api/v1/formsList all available forms (simplified structure)
| Parameter | Type | Description |
|---|---|---|
entityType | string | Filter by entity type |
localityType | string | Filter by locality type |
locality | string | Filter by specific locality |
since | string | ISO 8601 date - get forms modified since this date |
{
"forms": [
{
"formNumber": "1040",
"formName": "U.S. Individual Income Tax Return",
"localityType": "federal",
"locality": "United States",
"entityType": "individual",
"parentFormNumbers": ["1040"],
"owner": "ODOV",
"calculationBase": "end",
"calculationRules": [
{
"effectiveYears": [2019, 2021, 2022, 2023, 2024],
"dueDate": {
"monthsAfterCalculationBase": 4,
"dayOfMonth": 15
},
"extensions": {
"1": {
"formNumber": "4868",
"formName": "Application for Automatic Extension of Time to File U.S. Individual Income Tax Return",
"monthsAfterCalculationBase": 10,
"dayOfMonth": 15,
"extensionType": "automatic"
}
}
}
]
}
],
"lastUpdated": "2024-01-15T10:30:00Z",
"version": "1.0",
"count": 150
}curl -X GET "https://tax-forms.odov.io/api/v1/forms?entityType=individual" \
-H "X-API-Key: your-api-key-here"const response = await fetch('https://tax-forms.odov.io/api/v1/forms?entityType=individual', {
headers: {
'X-API-Key': 'your-api-key-here'
}
});
const data = await response.json();
console.log(data);import requests
response = requests.get(
'https://tax-forms.odov.io/api/v1/forms',
headers={'X-API-Key': 'your-api-key-here'},
params={'entityType': 'individual'}
)
data = response.json()
print(data)require 'net/http'
require 'json'
uri = URI('https://tax-forms.odov.io/api/v1/forms')
uri.query = URI.encode_www_form({entityType: 'individual'})
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['X-API-Key'] = 'your-api-key-here'
response = http.request(request)
data = JSON.parse(response.body)
puts data/api/v1/forms/{formNumber}Get form details by form number
{
"formNumber": "1040",
"variations": [
{
"formNumber": "1040",
"formName": "U.S. Individual Income Tax Return",
"entityType": "individual",
"localityType": "federal",
"locality": "United States"
}
],
"count": 1
}curl -X GET "https://tax-forms.odov.io/api/v1/forms/1040" \
-H "X-API-Key: your-api-key-here"const response = await fetch('https://tax-forms.odov.io/api/v1/forms/1040', {
headers: {
'X-API-Key': 'your-api-key-here'
}
});
const data = await response.json();
console.log(data);import requests
response = requests.get(
'https://tax-forms.odov.io/api/v1/forms/1040',
headers={'X-API-Key': 'your-api-key-here'}
)
data = response.json()
print(data)require 'net/http'
require 'json'
uri = URI('https://tax-forms.odov.io/api/v1/forms/1040')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['X-API-Key'] = 'your-api-key-here'
response = http.request(request)
data = JSON.parse(response.body)
puts data/api/v1/batchCalculate multiple forms in one request
{
"calculations": [
{
"formNumber": "1040",
"entityType": "individual",
"localityType": "federal",
"locality": "United States",
"coverageStartDate": "2024-01-01",
"coverageEndDate": "2024-12-31"
},
{
"formNumber": "941",
"entityType": "business",
"localityType": "federal",
"locality": "United States",
"coverageStartDate": "2024-01-01",
"coverageEndDate": "2024-03-31"
}
]
}{
"results": [
{
"formNumber": "1040",
"entityType": "individual",
"localityType": "federal",
"locality": "United States",
"coverageStartDate": "2024-01-01",
"coverageEndDate": "2024-12-31",
"success": true,
"dueDate": "2025-04-15",
"extensionDueDate": "2025-10-15"
}
],
"summary": {
"total": 1,
"successful": 1,
"failed": 0
}
}curl -X POST https://tax-forms.odov.io/api/v1/batch \
-H "X-API-Key: your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"calculations": [
{
"formNumber": "1040",
"entityType": "individual",
"localityType": "federal",
"locality": "United States",
"coverageStartDate": "2024-01-01",
"coverageEndDate": "2024-12-31"
}
]
}'const response = await fetch('https://tax-forms.odov.io/api/v1/batch', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key-here',
'Content-Type': 'application/json'
},
body: JSON.stringify({
calculations: [
{
formNumber: '1040',
entityType: 'individual',
localityType: 'federal',
locality: 'United States',
coverageStartDate: '2024-01-01',
coverageEndDate: '2024-12-31'
}
]
})
});
const data = await response.json();
console.log(data);import requests
response = requests.post(
'https://tax-forms.odov.io/api/v1/batch',
headers={
'X-API-Key': 'your-api-key-here',
'Content-Type': 'application/json'
},
json={
'calculations': [
{
'formNumber': '1040',
'entityType': 'individual',
'localityType': 'federal',
'locality': 'United States',
'coverageStartDate': '2024-01-01',
'coverageEndDate': '2024-12-31'
}
]
}
)
data = response.json()
print(data)require 'net/http'
require 'json'
uri = URI('https://tax-forms.odov.io/api/v1/batch')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['X-API-Key'] = 'your-api-key-here'
request['Content-Type'] = 'application/json'
request.body = {
calculations: [
{
formNumber: '1040',
entityType: 'individual',
localityType: 'federal',
locality: 'United States',
coverageStartDate: '2024-01-01',
coverageEndDate: '2024-12-31'
}
]
}.to_json
response = http.request(request)
data = JSON.parse(response.body)
puts dataConfigure webhooks to receive automated notifications when tax forms are updated or added. Perfect for keeping your systems in sync without constant polling.
When forms are updated, we'll send a POST request to your webhook URL:
POST https://your-webhook-url.com/endpoint
Content-Type: application/json
X-ODOV-Event: forms.sync
X-ODOV-API-Key: odov_k3y...
{
"event": "forms.sync",
"timestamp": "2024-03-15T14:30:00Z",
"data": {
"changedForms": [
{
"formNumber": "1040",
"formName": "U.S. Individual Income Tax Return",
"entityType": "individual",
"locality": "United States",
"localityType": "federal",
"lastModified": "2024-03-15T10:00:00Z"
}
],
"changesSince": "2024-03-14T14:30:00Z",
"totalChanges": 1
}
}| Header | Description |
|---|---|
X-ODOV-Event | Event type (currently only "forms.sync") |
X-ODOV-API-Key | Partial API key for identification |
Your endpoint should return a 2xx status code to acknowledge receipt. Any other status code or timeout will be considered a failure.
# This is what ODOV sends to your webhook:
curl -X POST https://your-webhook-url.com/endpoint \
-H "Content-Type: application/json" \
-H "X-ODOV-Event: forms.sync" \
-H "X-ODOV-API-Key: odov_k3y..." \
-d '{
"event": "forms.sync",
"timestamp": "2024-03-15T14:30:00Z",
"data": {
"changedForms": [...],
"changesSince": "2024-03-14T14:30:00Z",
"totalChanges": 1
}
}'// Express.js webhook handler
app.post('/webhook/odov', (req, res) => {
const event = req.headers['x-odov-event'];
const apiKeyPrefix = req.headers['x-odov-api-key'];
if (event === 'forms.sync') {
const { changedForms, changesSince } = req.body.data;
// Process the changed forms
changedForms.forEach(form => {
console.log(`Form ${form.formNumber} was updated`);
// Update your database, trigger workflows, etc.
});
// Acknowledge receipt
res.status(200).json({ received: true });
} else {
res.status(400).json({ error: 'Unknown event type' });
}
});# Flask webhook handler
from flask import Flask, request, jsonify
@app.route('/webhook/odov', methods=['POST'])
def handle_odov_webhook():
event = request.headers.get('X-ODOV-Event')
api_key_prefix = request.headers.get('X-ODOV-API-Key')
if event == 'forms.sync':
data = request.json['data']
changed_forms = data['changedForms']
# Process the changed forms
for form in changed_forms:
print(f"Form {form['formNumber']} was updated")
# Update your database, trigger workflows, etc.
# Acknowledge receipt
return jsonify({'received': True}), 200
else:
return jsonify({'error': 'Unknown event type'}), 400# Sinatra webhook handler
require 'sinatra'
require 'json'
post '/webhook/odov' do
request.body.rewind
payload = JSON.parse(request.body.read)
event = request.env['HTTP_X_ODOV_EVENT']
api_key_prefix = request.env['HTTP_X_ODOV_API_KEY']
if event == 'forms.sync'
changed_forms = payload['data']['changedForms']
# Process the changed forms
changed_forms.each do |form|
puts "Form #{form['formNumber']} was updated"
# Update your database, trigger workflows, etc.
end
# Acknowledge receipt
status 200
{ received: true }.to_json
else
status 400
{ error: 'Unknown event type' }.to_json
end
endchangesSince timestamp to detect gapsThe API uses standard HTTP status codes and returns detailed error information:
{
"error": "Validation failed",
"details": {
"formNumber": "Form not found",
"entityType": "Invalid entity type"
}
}| Status Code | Description |
|---|---|
200 | Request successful |
400 | Bad request - Invalid parameters |
401 | Unauthorized - Invalid or missing API key |
404 | Not found - Form or resource doesn't exist |
429 | Too many requests - Rate limit exceeded |
500 | Internal server error |