POST /v1/batch/geocode
Overview
Geocode up to 10,000 addresses in one asynchronous job: submit the list, poll the job status, download the results as CSV. Ideal for cleaning a customer file or preparing a dataset without writing a rate-limit-aware loop yourself.
Cost: 1 credit per address, debited at submission; failed lines are automatically refunded when the job completes. Availability: from the Growth plan.
The flow:
POST /v1/batch/geocode→202 Acceptedwith ajob_id(credits debited here)GET /v1/batch/{job_id}→ status and progress (free, poll as often as you like)GET /v1/batch/{job_id}/result→ results as CSV, oncestatusiscompleted
Results are kept for 7 days after submission (expires_at in the status response), then deleted.
Submit a job
POST https://api.geocodeme.net/v1/batch/geocode
Required header: X-Api-Key: gm_YOUR_API_KEY
Request Body
Two accepted formats:
- JSON (
Content-Type: application/json):{"addresses": ["18 rue de la Paix, Paris", "..."]} - CSV / plain text (
Content-Type: text/csvortext/plain): one address per line, UTF-8
Limits: 10,000 addresses max per job, 512 characters max per address. Empty lines are ignored. The submission is refused with a 429 QUOTA_EXCEEDED if your remaining credit window cannot cover the whole job; nothing is debited in that case.
Code Examples
- cURL (JSON)
- cURL (CSV)
- Python (full flow)
curl -X POST \
-H "X-Api-Key: gm_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"addresses": [
"18 rue de la Paix, Paris",
"10 Rue de Rivoli, Paris",
"1 Parvis de Notre-Dame, Paris"
]
}' \
"https://api.geocodeme.net/v1/batch/geocode"
curl -X POST \
-H "X-Api-Key: gm_YOUR_API_KEY" \
-H "Content-Type: text/csv" \
--data-binary @addresses.csv \
"https://api.geocodeme.net/v1/batch/geocode"
Where addresses.csv contains one address per line:
18 rue de la Paix, Paris
10 Rue de Rivoli, Paris
1 Parvis de Notre-Dame, Paris
import os
import time
import requests
api_key = os.environ["GEOCODEME_API_KEY"]
headers = {"X-Api-Key": api_key}
base = "https://api.geocodeme.net/v1/batch"
# 1. Submit
response = requests.post(
f"{base}/geocode",
json={"addresses": ["18 rue de la Paix, Paris", "10 Rue de Rivoli, Paris"]},
headers=headers,
)
response.raise_for_status()
job_id = response.json()["job_id"]
# 2. Poll until completed
while True:
status = requests.get(f"{base}/{job_id}", headers=headers).json()
if status["status"] in ("completed", "failed"):
break
print(f"{status['processed_lines']}/{status['total_lines']} processed...")
time.sleep(2)
# 3. Download the CSV
result = requests.get(f"{base}/{job_id}/result", headers=headers)
result.raise_for_status()
with open("geocoded.csv", "wb") as f:
f.write(result.content)
Response: 202 Accepted
{
"job_id": "0198c9a2-7f43-7b21-a5d0-3f6e2c9b8d11",
"status": "queued",
"total_lines": 3,
"credits_debited": 3
}
| Field | Type | Description |
|---|---|---|
job_id | string (UUID) | Job identifier: use it for the status and result endpoints |
status | string | Always queued at submission |
total_lines | integer | Number of addresses accepted |
credits_debited | integer | Credits debited now (1 per address) |
Poll the status: GET /v1/batch/{job_id}
Polling is free (no credit debit, only rate-limited).
curl -H "X-Api-Key: gm_YOUR_API_KEY" \
"https://api.geocodeme.net/v1/batch/0198c9a2-7f43-7b21-a5d0-3f6e2c9b8d11"
{
"job_id": "0198c9a2-7f43-7b21-a5d0-3f6e2c9b8d11",
"status": "completed",
"total_lines": 3,
"processed_lines": 3,
"failed_lines": 1,
"credits_debited": 3,
"created_at": "2026-07-20T09:14:03.201000+00:00",
"finished_at": "2026-07-20T09:14:41.877000+00:00",
"expires_at": "2026-07-27T09:14:03.201000+00:00",
"error": null
}
| Field | Type | Description |
|---|---|---|
status | string | queued, running, completed, or failed |
total_lines | integer | Total number of addresses in the job |
processed_lines | integer | Addresses processed so far |
failed_lines | integer | Addresses that could not be geocoded (refunded) |
credits_debited | integer | Credits debited at submission |
created_at | string | Submission time (ISO 8601, UTC) |
finished_at | string | null | Completion time: null while the job runs |
expires_at | string | When the results are deleted (7 days after submission) |
error | string | null | Failure reason when status is failed |
When the job completes, every failed line is refunded (1 credit each). If the job fails entirely, every line that was not successfully geocoded is refunded.
Download the results: GET /v1/batch/{job_id}/result
Available once status is completed. Streams a CSV file (also free of credits):
curl -H "X-Api-Key: gm_YOUR_API_KEY" -o geocoded.csv \
"https://api.geocodeme.net/v1/batch/0198c9a2-7f43-7b21-a5d0-3f6e2c9b8d11/result"
input,status,lat,lon,display_name
"18 rue de la Paix, Paris",ok,48.8687,2.3315,"18, Rue de la Paix, 2e Arrondissement, Paris, Île-de-France, 75002, France"
"10 Rue de Rivoli, Paris",ok,48.8556,2.3603,"10, Rue de Rivoli, 4e Arrondissement, Paris, Île-de-France, 75004, France"
"zzzz nowhere",failed,,,
| Column | Description |
|---|---|
input | The address as you submitted it |
status | ok or failed |
lat / lon | Coordinates of the best match (empty when failed) |
display_name | Full formatted address of the best match (empty when failed) |
Error Responses
Results requested before completion
{
"error": {
"code": "JOB_NOT_READY",
"message": "Job is 'running': results are only available once completed"
}
}
Returned with HTTP status 409. Keep polling the status endpoint until status is completed.
Unknown job (or a job owned by another account)
{
"error": {
"code": "NOT_FOUND",
"message": "Batch job not found"
}
}
Jobs are only visible to the account that created them; a foreign job_id returns 404, never 403.
Plan without batch access
{
"error": {
"code": "PLAN_UPGRADE_REQUIRED",
"message": "The 'batch' feature is not included in your plan. Upgrade to unlock it.",
"upgrade_url": "https://geocodeme.net/dashboard/billing"
}
}
Oversized job
{
"error": {
"code": "INVALID_PARAMETER",
"message": "Too many addresses (12000 > 10000)",
"field": "addresses"
}
}
All endpoints are also subject to INVALID_API_KEY (401) and RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED (429) errors. See Error Codes for the full reference.