API Documentation
Integrate our barcode lookup API into your applications
Introduction
The Barcode Report API looks up product data by UPC, EAN, GTIN or ISBN across a catalogue of more than 500 million barcodes. It is a read-only JSON API over HTTPS; every request needs an API key.
The base URL is https://barcodereport.com/api. Create an account to get a key — every new account includes 100 lookups a month at no cost, and your key is on your dashboard the moment you sign up.
Authentication
Send your API key in a header. Either of these works:
Authorization: Bearer YOUR_API_KEY
# or
X-API-Key: YOUR_API_KEY
A legacy ?api_key= query parameter is still accepted so existing integrations keep working, but prefer a header: query strings end up in browser history, proxy logs and referrer headers.
You can replace your key at any time from the dashboard. Rotation takes effect immediately — the previous key starts returning invalid_key straight away (401 on /api/v1/*, 403 on the legacy paths).
Search a barcode
GET/api/search_barcode
Looks up one barcode. Matches against both the current barcode and any superseded one, so an ISBN-10 resolves to the same record as its ISBN-13.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
barcode | query | Yes | The code to look up. Maximum 32 characters. |
api_key | query | Legacy | Only if you are not sending an authentication header. |
Example request
curl "https://barcodereport.com/api/search_barcode?barcode=747062088118" \
-H "X-API-Key: YOUR_API_KEY"
Example response — 200
{
"barcode": "747062088118",
"title": "Replacement for UL2354 BATTERY UL2354 Replacement Battery",
"brand": "CHOSEN SUPPLIES",
"category": "Electronics > Electronics Accessories > Power > Batteries",
"description": "Manufacturer: CHOSEN SUPPLIES.",
"image_url": "https://example.com/images/747062088118.jpg",
"min_price": "43.19",
"price_currency": "USD",
"old_barcode": null,
"authors": null,
"number_of_pages": null,
"publish_date": null,
"isbn": null,
"ean_13": null,
"upc_a": null,
"gtin_14": null,
"model_number": null,
"color": null,
"size": null,
"weight": null,
"product_dimension": null,
"amazon_asin": null
}
Response fields
| Field | Type | Notes |
|---|---|---|
barcode | string | The canonical code for this product. |
old_barcode | string · null | A superseded code, typically the ISBN-10 for a book. |
title | string | Product name. Populated for effectively every record. |
brand | string · null | Brand or manufacturer. |
category | string · null | Category path, >-delimited where we have one. |
description | string · null | Free-text product description. |
image_url | string · null | Product image, hosted externally. |
min_price / price_currency | string · null | Lowest price we have seen, and its currency. |
authors, number_of_pages, publish_date, isbn, isbn_publisher | string · null | Book fields; set for ISBN records. |
upc_a, ean_13, gtin_14, amazon_asin, model_number | string · null | Alternative identifiers, where known. |
color, size, weight, product_dimension | string · null | Physical attributes, where known. |
Every field except barcode and title may be null. Coverage is deepest on title and thins out across the optional attributes, so treat anything else as best-effort and code defensively. We may add fields over time; we will not remove or rename existing ones, so parse leniently and ignore keys you do not recognise.
Lookups that find nothing still count. A 404 consumes one request from your monthly allowance, because the search runs either way. Your dashboard shows your match rate over the last 14 days so you can see how well our catalogue covers the products you look up.
Batch lookup
POST/api/v1/lookup
Looks up as many as 100 barcodes in one request. Each distinct code costs one lookup, and the call is all-or-nothing against your allowance: if the batch would take you over your limit, it is refused and nothing is consumed. Duplicates and empty values are ignored.
curl -X POST "https://barcodereport.com/api/v1/lookup" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"barcodes": ["049000050103", "3017620422003", "860999888771"]}'
{
"count": 3,
"found": 2,
"results": [
{
"requested_barcode": "049000050103",
"found": true,
"product": {
"barcode": "049000050103",
"title": "Coca-cola bottle, 2 liters",
"brand": "Coca-Cola",
"…": "…"
}
},
{
"requested_barcode": "3017620422003",
"found": true,
"product": {
"barcode": "3017620422003",
"title": "Nutella Ferrero Hazelnut Chocolate Spread 400g",
"…": "…"
}
},
{
"requested_barcode": "860999888771",
"found": false,
"product": null
}
]
}
Each product has the same fields as /api/search_barcode. Batch and search calls are limited to a fifth of your plan's per-minute rate, with a minimum of 30 requests a minute.
Product search
GET/api/v1/search?q=…
Full-text search over product title, brand and category, when you have a name rather than a barcode. One call costs one lookup whatever the page size.
| Name | In | Required | Description |
|---|---|---|---|
q | query | Yes | Search text, up to 120 characters. |
page | query | No | Page number, 1 to 100. Default 1. |
per_page | query | No | Results per page, 1 to 50. Default 20. |
{
"query": "nutella",
"page": 1,
"per_page": 20,
"has_more": true,
"results": [
{
"barcode": "3017620422003",
"title": "Nutella Ferrero Hazelnut Chocolate Spread 400g",
"brand": "Ferrero",
"category": "Food, Beverages & Tobacco",
"image_url": "…"
}
]
}
/api/v1/* endpoints use conventional status codes: 401 for a missing or invalid key and 402 for quota_exceeded. The legacy /api/* endpoints keep 400/403 for compatibility. The JSON code field is the same on both.
Account and quota
GET/api/account
Returns your plan and how much of your allowance is left. This endpoint does not consume a request, so you can poll it safely.
{
"name": "Jane Doe",
"email": "[email protected]",
"plan_name": "Standard Plan",
"plan_limit": 10000,
"api_usage": 1432,
"usage_remaining": 8568,
"usage_reset_at": "Wed, 22 Jul 2026 14:39:00 GMT",
"created_at": "Tue, 20 Jan 2026 14:51:02 GMT"
}
Successful lookups also carry X-Quota-Limit and X-Quota-Remaining headers, so in normal operation you can track your allowance without a second call.
Rate limits
Limits are applied per API key, per minute. They are separate from your monthly allowance: the rate limit controls how fast you may call, the plan limit controls how much in total.
| Plan | Single lookups | Batch and search |
|---|---|---|
| Free | 60 requests / minute | 30 requests / minute |
| Standard Plan | 300 requests / minute | 60 requests / minute |
| Premium Plan | 600 requests / minute | 120 requests / minute |
| Professional Plan | 1,200 requests / minute | 240 requests / minute |
| Enterprise Plan | 2,400 requests / minute | 480 requests / minute |
/api/account is limited to 30 requests / minute on every plan. | ||
"Single lookups" means /api/search_barcode and /api/v1/search_barcode; "batch and search" means POST /api/v1/lookup and GET /api/v1/search. A batch of 100 codes is still one request against the rate limit, so batching is the fastest way to work through a large list.
Every response includes X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Exceeding a limit returns 429 with a Retry-After header giving the number of seconds to wait — back off for that long rather than retrying immediately.
Errors
Errors return a JSON body with a human-readable error and a stable machine-readable code. Branch on code, never on the message text — wording may change, codes will not.
{
"error": "You have reached the maximum usage limit. Please upgrade your plan",
"code": "quota_exceeded"
}
/api/v1/* | Legacy /api/* | Code | What it means |
|---|---|---|---|
400 | 400 | bad_request | A required parameter is missing, empty or malformed (for example a barcode longer than 32 characters, or more than 100 codes in a batch). |
401 | 400 | missing_key | No API key was sent in a header or the query string. |
401 | 403 | invalid_key | The key is not recognised. Check it on your dashboard, or rotate it. |
402 | 403 | quota_exceeded | Your monthly allowance is used up, or your account has no active plan. Upgrade or wait for the reset. The response carries a Link header to the upgrade page. |
404 | 404 | not_found | We have no record for that barcode. Counts against your allowance. |
429 | 429 | rate_limited | Too many requests per minute. Wait for Retry-After seconds. |
500 | 500 | server_error | Something failed on our side. Safe to retry with backoff; the lookup is not charged. |
On the legacy paths invalid_key and quota_exceeded share 403 but mean very different things: one needs a new key, the other needs a bigger plan. Branch on code and the difference is never ambiguous.
OpenAPI, Postman, MCP and Sheets
- OpenAPI 3 spec: /openapi.json, for code generators, API clients and coding assistants.
- Postman collection: download, then set the
apiKeyvariable. - MCP server for Claude, ChatGPT, Cursor and VS Code:
https://barcodereport.com/api/mcp. Setup and tools. - Google Sheets:
=BARCODELOOKUP(A2:A100, "title,brand"). Get the script.
All of them use the same API key and allowance as the endpoints above.
Code Examples
import requests
API_KEY = "your_api_key"
response = requests.get(
"https://barcodereport.com/api/search_barcode",
params={"barcode": "747062088118"},
headers={"X-API-Key": API_KEY},
timeout=10,
)
if response.status_code == 200:
product = response.json()
print(product["title"], product.get("brand"))
elif response.status_code == 404:
print("No record for that barcode")
else:
# Branch on the stable code, not the message.
print("Error:", response.json().get("code"))
const API_KEY = "your_api_key";
const res = await fetch(
"https://barcodereport.com/api/search_barcode?barcode=747062088118",
{ headers: { "X-API-Key": API_KEY } }
);
const data = await res.json();
if (res.ok) {
console.log(data.title, data.brand);
} else if (data.code === "rate_limited") {
const wait = Number(res.headers.get("Retry-After") ?? 60);
console.warn(`Backing off for ${wait}s`);
} else {
console.error(data.code, data.error);
}
$ch = curl_init("https://barcodereport.com/api/search_barcode?barcode=747062088118");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-Key: your_api_key"],
CURLOPT_TIMEOUT => 10,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($body, true);
if ($status === 200) {
echo $data["title"];
} else {
echo "Error: " . $data["code"];
}
require "net/http"
require "json"
uri = URI("https://barcodereport.com/api/search_barcode")
uri.query = URI.encode_www_form(barcode: "747062088118")
request = Net::HTTP::Get.new(uri)
request["X-API-Key"] = "your_api_key"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
data = JSON.parse(response.body)
puts response.code == "200" ? data["title"] : data["code"]
Ready to Get Started?
Create a free account and your API key is waiting on your dashboard — 100 lookups included, no card required.
Get your API key