Skip to content
Products
Developers & lookup

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:

Recommended
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

NameInRequiredDescription
barcodequeryYesThe code to look up. Maximum 32 characters.
api_keyqueryLegacyOnly if you are not sending an authentication header.

Example request

cURL
curl "https://barcodereport.com/api/search_barcode?barcode=747062088118" \
  -H "X-API-Key: YOUR_API_KEY"

Example response — 200

JSON
{
  "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

FieldTypeNotes
barcodestringThe canonical code for this product.
old_barcodestring · nullA superseded code, typically the ISBN-10 for a book.
titlestringProduct name. Populated for effectively every record.
brandstring · nullBrand or manufacturer.
categorystring · nullCategory path, >-delimited where we have one.
descriptionstring · nullFree-text product description.
image_urlstring · nullProduct image, hosted externally.
min_price / price_currencystring · nullLowest price we have seen, and its currency.
authors, number_of_pages, publish_date, isbn, isbn_publisherstring · nullBook fields; set for ISBN records.
upc_a, ean_13, gtin_14, amazon_asin, model_numberstring · nullAlternative identifiers, where known.
color, size, weight, product_dimensionstring · nullPhysical 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
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"]}'
Response — 200
{
    "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.

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.

JSON
{
  "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.

PlanSingle lookupsBatch and search
Free60 requests / minute30 requests / minute
Standard Plan300 requests / minute60 requests / minute
Premium Plan600 requests / minute120 requests / minute
Professional Plan1,200 requests / minute240 requests / minute
Enterprise Plan2,400 requests / minute480 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.

JSON
{
  "error": "You have reached the maximum usage limit. Please upgrade your plan",
  "code": "quota_exceeded"
}
/api/v1/*Legacy /api/*CodeWhat it means
400400bad_requestA required parameter is missing, empty or malformed (for example a barcode longer than 32 characters, or more than 100 codes in a batch).
401400missing_keyNo API key was sent in a header or the query string.
401403invalid_keyThe key is not recognised. Check it on your dashboard, or rotate it.
402403quota_exceededYour 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.
404404not_foundWe have no record for that barcode. Counts against your allowance.
429429rate_limitedToo many requests per minute. Wait for Retry-After seconds.
500500server_errorSomething 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 apiKey variable.
  • 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

Python
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"))
JavaScript (Node)
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);
}
PHP
$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"];
}
Ruby
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