Barcode Lookup API in Python: Get Product Details from UPC, EAN, and ISBN Codes

Published October 7, 2026

Listing automation, scan-to-add inventory, price comparison and book cataloging all start from the same primitive: turn the number under a barcode into structured product data. This barcode lookup API Python tutorial uses the Barcode Lookup API, which does exactly that for UPC-A, EAN-13, JAN and ISBN-10/13 codes — and doubles as a keyword search over a catalog of 500+ million products, all through one GET endpoint.

Endpoint overview

The project has a single operation with two modes, selected by the shape of the query parameter:

Modequery valueReturns
Barcode lookupNumeric code: UPC-A, EAN-13, JAN, ISBN-10/13One product object with full details
Keyword searchPlain-text term, e.g. nutellaPaginated results array plus a nextPage cursor

The optional page parameter applies only to search mode; leave it empty for barcode lookups. A code with no catalog record returns HTTP 200 with an empty results array, so absence is easy to branch on.

Step 1 — Prepare an authorized platform key

Sign in and copy an active key from your dashboard. Python and JavaScript below read it from the OPENDATA_API_KEY environment variable; the curl examples use the YOUR_API_KEY placeholder. Query URLs contain your key, so keep them out of shared links, screenshots and logs, and run production calls server-side. Opening documentation or copying an example never sends a data request — deliberately running one does.

Two status layers apply to every response: the HTTP status, and the gateway's code/message/data business status. code=200 indicates gateway business success; HTTP 200 alone does not.

Step 2 — Look up a barcode

Pass a numeric code as query to search-product-details:

curl --get 'https://api.opendata-api.com/api/project/barcodes-lookup/search-product-details?key=YOUR_API_KEY' \
  --data-urlencode 'query=3017620422003'

ISBNs go through the same parameter, so a book catalog app needs no separate books API:

curl --get 'https://api.opendata-api.com/api/project/barcodes-lookup/search-product-details?key=YOUR_API_KEY' \
  --data-urlencode 'query=9780747532699'

On a hit, data.product carries title, description, a category path array, brand and manufacturer, an attributes map (mpn, asin, size, dimensions, weight when known), a features list, images, and an author field on books and media. Food items may add nutrition_facts and ingredients. barcode_formats echoes the matched codes (upc_a, ean_13, isbn_10, isbn_13 as applicable), and many records include an online_stores array with per-store name, price and url — a ready-made cross-store join key.

Field depth varies by product. Popular items carry full attributes and store offers; obscure records may return only a sparse title. Branch on field presence instead of assuming any field is populated.

Step 3 — Keyword search with pagination

Pass a text term to switch modes:

curl --get 'https://api.opendata-api.com/api/project/barcodes-lookup/search-product-details?key=YOUR_API_KEY' \
  --data-urlencode 'query=nutella'

Search mode returns a results array — each hit includes barcode, title, category, image and manufacturer — plus a nextPage cursor. Pass the returned value back as page to fetch the next batch:

curl --get 'https://api.opendata-api.com/api/project/barcodes-lookup/search-product-details?key=YOUR_API_KEY' \
  --data-urlencode 'query=nutella' \
  --data-urlencode 'page=2'

Stop when nextPage comes back null, never invent page numbers, and deduplicate hits by barcode across pages.

Step 4 — Scan-to-enrich in Python

This script turns a list of scanned barcodes into catalog rows with the best available store price. It checks both status layers, treats an empty results array as a clean miss, and paces requests politely:

import os
import time

import requests

BASE = "https://api.opendata-api.com/api/project/barcodes-lookup"
API_KEY = os.environ["OPENDATA_API_KEY"].strip()


def call(**params):
    """Call the endpoint and unwrap the gateway envelope."""
    response = requests.get(
        f"{BASE}/search-product-details",
        params={"key": API_KEY, **params},
        timeout=30,
    )
    response.raise_for_status()  # HTTP layer: 4xx/5xx is transport or quota trouble
    envelope = response.json()
    if envelope.get("code") != 200:
        raise RuntimeError(f"Gateway business failure: {envelope.get('message')}")
    return envelope.get("data")


def lookup_barcode(code):
    """Return the product dict for a barcode, or None on a clean miss."""
    data = call(query=code)  # page stays empty in barcode mode
    if not isinstance(data, dict):
        return None
    product = data.get("product")
    return product if isinstance(product, dict) else None


def best_offer(stores):
    """Pick the lowest numeric price across per-store offers."""
    offers = []
    for store in stores or []:
        try:
            offers.append((float(store["price"]), store.get("name"), store.get("url")))
        except (KeyError, TypeError, ValueError):
            continue  # skip offers without a usable price
    return min(offers) if offers else (None, None, None)


def enrich(barcodes, pause=1.0):
    """Resolve barcodes into catalog rows with the best observed price."""
    rows = []
    for code in barcodes:
        product = lookup_barcode(code)
        if product is None:
            rows.append({"barcode": code, "found": False})
        else:
            price, store, _url = best_offer(product.get("online_stores"))
            rows.append({
                "barcode": code,
                "found": True,
                "title": product.get("title"),
                "brand": product.get("brand"),
                "category": " > ".join(product.get("category") or []),
                "asin": (product.get("attributes") or {}).get("asin"),
                "best_price": price,
                "best_store": store,
            })
        time.sleep(pause)  # polite pacing between calls
    return rows


for row in enrich(["3017620422003", "9780747532699"]):
    print(row)

A miss is a result, not an exception: found: False rows are candidates for manual entry or a later re-check, not for retries in a tight loop. Write the rows to CSV and you have a PIM/ERP import.

The JavaScript core of one lookup:

const BASE = "https://api.opendata-api.com/api/project/barcodes-lookup";
const key = process.env.OPENDATA_API_KEY;

async function lookupBarcode(code) {
  const params = new URLSearchParams({ key, query: code });
  const res = await fetch(`${BASE}/search-product-details?${params}`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const gateway = await res.json();
  if (gateway.code !== 200) throw new Error(`Business failure: ${gateway.message}`);
  return gateway.data?.product ?? null; // null on a clean miss
}

Response fields worth keeping

FieldMeaning
productProduct object on a barcode hit
product.title, product.descriptionName and long description
product.barcode_formatsEchoes matched codes: upc_a, ean_13, isbn_10, isbn_13
product.categoryCategory path array, broadest first
product.brand, product.manufacturerBrand and manufacturer names
product.attributesmpn, asin, size, dimensions, weight when known
product.featuresFeature bullets for listing prefill
product.nutrition_facts, product.ingredientsFood records only
product.imagesImage URLs
product.online_storesPer-store name, price, url offers
resultsSearch-mode hits: barcode, title, category, image, manufacturer
nextPageCursor to pass back as page; null when no more pages

Use cases

  • Scan-to-add inventory — warehouse and POS apps scan a UPC/EAN and auto-fill stock records with title, category, manufacturer and images.
  • Marketplace listing prefill — prefill listings from a scanned barcode with description, MPN/ASIN attributes and weight/size.
  • Price comparison — use the barcode as a cross-store join key and compare offers from online_stores.
  • Book cataloging — ISBN lookups return title, author and cover images for library and used-bookstore scanning.
  • Grocery and calorie tracking — scan a food EAN-13 to pull nutrition facts and images for meal logging.
  • Bulk catalog enrichment — resolve CSVs of barcodes into structured PIM/ERP import rows.

FAQ

How do I get product information from a barcode number programmatically?

Pass the code as query to search-product-details — UPC-A, EAN-13, JAN and ISBN all go through the same parameter. On a hit, data.product carries the structured record.

What API returns product title and image for a UPC code?

This one: a UPC lookup returns product.title plus product.images, with brand, category and attributes alongside.

How do I convert UPC to ASIN with an API?

When the matched record is known, its attributes map carries asin. Treat it as enrichment — not every record has one — rather than a dedicated converter.

How do I build a barcode scanning inventory app?

Feed each scanned code into lookup_barcode from Step 4, map the returned fields onto your stock table, and route clean misses to a manual-entry flow.

What is the best ISBN lookup API for a book catalog app?

One that returns title, author and cover from ISBN-10/13 without a separate books account. This endpoint does that through the same query parameter, and barcode_formats echoes the matched ISBN forms.

How do I get nutrition facts from a food barcode API?

Food records may carry nutrition_facts and ingredients; branch on field presence, because sparse records return only a title.

How do I search a product database by keyword and paginate results?

Pass a text term as query, then follow the nextPage cursor as the page parameter until it comes back null, as shown in Step 3.

Is there an API to look up EAN-13 barcodes for European products?

EAN-13 resolves through the same parameter as UPC. Coverage varies by product; an empty results array means the code is not in the catalog, not that the request failed.

How do I enrich a product catalog from a list of barcodes?

Run the scan-to-enrich script over your list: each row keeps the barcode, found flag, title, brand, category path, ASIN when known, and the best observed store price.

How do price comparison sites match the same product across stores?

They join on the barcode. The online_stores array already lists per-store name, price and URL for a matched product, so one lookup per code feeds a comparison table.

Try the flow on the Barcode Lookup API page, or read the search-product-details reference. APIs for similar apps and catalogs — Amazon, Walmart, eBay, Google Shopping, Target, the GS1 GTIN registry, Open Food Facts, Open Library / ISBNdb, Taobao, 1688, JD, or ShopSavvy/PriceGrabber-style price feeds — can be added on request via the contact page, and private source-code deployment is available. See pricing for per-request costs.