Developers
API reference
Every route uses the same resolver. A valid GTIN becomes a retail barcode. An http or https URL, including a GS1 Digital Link, becomes a QR code. Anything else needs a symbology name. The image address is public.
- Base URL
https://beepable.app- Format
- OpenAPI 3.1
- For agents
- llms.txt
Quickstart
-
Create a key
Sign in, open Account, and create a key. A picture key is for spreadsheets. An All routes key is for your own code.
-
Look up a value
Send the value. The answer names the barcode type and the address of its picture, without drawing it.
curl -H "Authorization: Bearer $BEEPABLE_KEY" \ "https://beepable.app/b/auto.json?v=4006381333931"{ "ok": true, "symbology": "ean13", "digitalLink": false, "imageUrl": "https://beepable.app/b/ean13.png?v=4006381333931", "print": { "minMm": 29.832, "targetMm": 37.29, "maxMm": 74.58, "note": "EAN-13 nominal size is 37.29 × 25.93 mm at 100%. Magnification is 80–200%." } }const response = await fetch("https://beepable.app/b/auto.json?v=4006381333931", { headers: { Authorization: `Bearer ${process.env.BEEPABLE_KEY}` }, }); const result = await response.json(); if (result.ok) console.log(result.symbology, result.imageUrl); else console.log(result.error.code, result.error.message);import os, requests response = requests.get( "https://beepable.app/b/auto.json", params={"v": "4006381333931"}, headers={"Authorization": f"Bearer {os.environ['BEEPABLE_KEY']}"}, ) result = response.json() print(result["imageUrl"] if result["ok"] else result["error"]["message"]) -
Show the picture
Add a picture key as
kand use the address anywhere an image goes.<img src="https://beepable.app/b/ean13.png?v=4006381333931&k=PICTURE_KEY" alt="EAN-13 4006381333931">=IMAGE("https://beepable.app/i/auto.png?v="&ENCODEURL(A2)&"&k="&$Z$1)
Keys
Create keys under Account. A key is shown once. A revoked key stops working on the next request.
| Key | Send it as | Opens |
|---|---|---|
| Picture key For spreadsheets, documents, and web pages | k in the address | Pictures: /i/, /b/{symbology}.png, /i/dl.png, /dl/pack.zip, /l/fnsku.png, /l/fnsku.pdf |
| All routes key For your own server | Authorization: Bearer | Every route, including /b/{symbology}.json, /excel, /sheets, /api/batch, /csv, /dl.json, /dl.csv, and /labels/ |
- Never put an All routes key in an image address. The address is public, and a CDN may keep it for a year.
- A picture key on a route that needs an All routes key is HTTP 403: This key can only draw pictures.
- A
pictureKeyorpkthat adds a key to the addresses must be a picture key of the same account. An All routes key there is HTTP 400: Use a picture key, not an All routes key, in image addresses. - A missing or unknown key is HTTP 401: This request needs an API key. A revoked key is: This API key has been revoked. On a picture route for a sheet, both are a PNG card instead.
- A request with
kcan be cached publicly when it succeeds. A request with onlyAuthorizationisprivate, no-store.
Endpoints
Open an endpoint to see its parameters and responses. Choose Authorize, paste a key, and use Try it out to call it from this page. The key stays in this browser tab: it is not saved, and it is gone when you reload.
Barcodes
One value in, one barcode or one decision out.
- GET
/i/{symbology}.pngDraw a picture for a spreadsheet cell - GET
/b/{symbology}.pngDraw a picture - GET
/b/{symbology}.jsonLook up a value: barcode type and image address
Spreadsheets
Text routes for Excel and Google Sheets. Most sheets need only the picture route, /i/{symbology}.png.
- GET
/excelGet the image address as text (Excel WEBSERVICE) - GET
/sheetsGet the image address as text (Apps Script) - GET
/sheets/barcode.gsGet the Apps Script sourceNo key
Files and batches
Up to 10,000 values per request. These return addresses, not pictures.
- POST
/api/batchLook up to 10,000 values - POST
/csvAdd image addresses to a CSV
GS1 Digital Link
Build a GS1 Digital Link from a GTIN, then draw its QR code.
- GET
/dl.jsonBuild a GS1 Digital Link - GET
/i/dl.pngDraw the QR code of a Digital Link - GET
/dl/pack.zipDownload the retail barcode and the QR code - POST
/dl.csvBuild Digital Links for a CSV
Amazon FNSKU labels
Print-ready Code 128 labels with the FNSKU, title, and condition.
- GET
/l/fnsku.pngDraw one FNSKU label - GET
/l/fnsku.pdfPrint copies of one label as a PDF - POST
/labels/fnsku.pdfPrint labels from a file - POST
/labels/fnsku.csvCheck a label file and add label addresses
Reference
Open routes that need no key.
- GET
/apiList the routesNo key - GET
/openapi.jsonGet this OpenAPI documentNo key - GET
/sample/{file}Get an open sample pictureNo key
Symbology names
Automatic mode accepts a valid GTIN-8, GTIN-12, GTIN-13, or GTIN-14, or an http or https URL. A 14-digit value with a leading zero stays ITF-14. An ISBN-13 stays EAN-13. A named retail symbology that does not fit is an error. The service does not switch to another symbology.
| Name | Accepts |
|---|---|
auto | Choose from the value. |
ean8 | 8 digits and a valid check digit. |
upca | 12 digits and a valid check digit. |
ean13 | 13 digits and a valid check digit. |
itf14 | 14 digits and a valid check digit. |
code128 | Basic Latin, at most 500 characters. A code point above 127 is rejected. |
qr | Any non-empty value within 2,000 characters, including a bad GTIN. |
datamatrix | Same as QR. Name it for a plain Data Matrix used inside a plant. It is not the retail checkout carrier. |
gs1datamatrix | A parenthesised element string that starts with (01) and a 14-digit GTIN. Dates are (15) and (17) as YYMMDD. Lot, serial, and CPV are (10), (21), and (22). Other application identifiers are refused. |
fnsku | An Amazon FNSKU. Automatic mode accepts 10 characters starting with X00. Named fnsku also accepts a B0 ASIN or an ISBN-10 shape. |
A Digital Link is two neighbouring path segments, on any host, after any prefix. The match uses the decoded segments. /8003/00952012345678 is a GRAI. /8003/10952012345678 is not, so that URL stays a QR code. Query-string keys stay a QR code. A bad check digit inside a pasted link is bad_check_digit_in_link. Build a link from a GTIN at /digital-link.
Errors
These sentences are exact. A JSON failure uses the code and the sentence. A text or file route sends the sentence as the whole body. A picture route for a sheet draws the sentence on a PNG card. Show the message to the person who entered the value. Two codes, wrong_length and bad_check_digit, are also used by the Digital Link builder with its own sentence, so branch on the code and display the message you received.
| Code | Sentence |
|---|---|
empty | Enter a GTIN or a URL. |
too_long | Value is longer than 2,000 characters. |
not_gtin_or_url | This is neither a valid GTIN, an Amazon FNSKU, nor a URL. Name a symbology, such as code128. |
not_fnsku | An Amazon FNSKU has 10 characters and usually starts with X00. Copy it from Seller Central. |
wrong_length | A retail barcode needs 8, 12, 13, or 14 digits. This has {n}. Name code128 to encode it anyway. |
bad_check_digit | Check digit is invalid for this GTIN. Name code128 to encode it anyway. |
bad_check_digit_in_link | The GS1 identifier in this Digital Link has an invalid check digit. Name qr to encode the URL anyway. |
symbology_mismatch | {Name} needs {n} digits. This has {count}. |
unsupported_characters | Code 128 can only encode basic Latin characters. |
cannot_encode | Code 128 can encode at most 500 characters. |
unknown_symbology | Unknown symbology. Use auto, ean8, upca, ean13, itf14, code128, qr, datamatrix, gs1datamatrix, or fnsku. |
bad_element_string | The element string must start with (01) and a 14-digit GTIN, and use only the AIs this service checks. |
bad_domain | The domain must be an https address, such as https://id.brand.com. |
wrong_length (Digital Link) | A GTIN needs 8, 12, 13, or 14 digits. This has {n}. |
bad_check_digit (Digital Link) | The check digit is invalid for this GTIN. |
bad_lot | The lot must be 1 to 20 characters from GS1 character set 82. Use digits, letters, and !"%&'()*+,-./:;<=>?_. |
bad_serial | The serial must be 1 to 20 characters from GS1 character set 82. Use digits, letters, and !"%&'()*+,-./:;<=>?_. |
bad_cpv | The CPV must be 1 to 20 characters from GS1 character set 82. Use digits, letters, and !"%&'()*+,-./:;<=>?_. |
bad_date | The date must be YYMMDD or YYYY-MM-DD. Use a month from 01 to 12. The day may be 00. |
unauthorized | This request needs an API key. A revoked key uses: This API key has been revoked. |
forbidden | This key can only draw pictures. |
rate_limited | Too many requests. Try again shortly. An upload can also answer: Too many uploads. Try again in a minute. Or: Another file is still being processed. Wait for it to finish. |
Label and file sentences
| Case | Sentence |
|---|---|
| FNSKU | An Amazon FNSKU has 10 characters and usually starts with X00. Copy it from Seller Central. |
| Condition | Unknown condition. Use New, Used - Like New, Used - Very Good, Used - Good, Used - Acceptable, Collectible, or Refurbished. |
| Quantity | Quantity must be a whole number from 1 to 1,000. |
| Title | The title is longer than 500 characters. |
| Title text | The title must be text. |
| Label width | A 10-character FNSKU does not fit on this label. |
| Template | Unknown label template. Use avery-5160, thermal-2x1, thermal-2.25x1.25, a4-21-63x38, a4-24-63x34, or a4-24-70x37. |
| Start | Start must be a whole number from 1 to {n}, where {n} is the number of labels on one sheet, such as 30 for Avery 5160. A roll has no start position: Start must be a whole number from 1. |
| Label CSV | The CSV needs an fnsku column. |
| No rows | The file has no label rows. |
| Label JSON | The JSON needs an items array. |
| Bad row | This row is not a label. |
| All failed | Every row failed. Fix the FNSKU, condition, or quantity and try again. |
| Barcode CSV | The CSV needs a value column. |
| Digital Link CSV | The CSV needs a gtin column. |
| Batch | The request needs an items array. |
Any method other than GET on a /b/ or /i/ route is HTTP 405. The renderer draws at most 32 pictures at a time. Up to 10,000 further misses wait. A miss that cannot enter that queue, or that has waited 60 seconds, is HTTP 429 with rate_limited. There is no per-address cap.
Limits
Limits are buckets that refill over time, so a short burst is fine. Pictures, decisions, and uploads are counted per account, never per network address, because Excel and Google Sheets fetch pictures from shared addresses. Only a picture that has to be drawn counts. A picture served from the cache is free, so reloading a sheet costs nothing.
Rate limits
| What | Limit |
|---|---|
| Pictures that have to be drawn | 20,000 per account at once, refilled at 300 per second. A sheet of 10,000 new barcodes fits in one burst. |
| Decisions, Excel, and Sheets text routes | 20,000 per account at once, refilled at 300 per second. |
| CSV and batch uploads | 30 per minute per account, and at most 2 at the same time. |
| Requests without a working key | 60 per minute per network address, except on picture routes, which always draw the key card. |
| Pages | 300 at once per network address, refilled at 10 per second. |
| Sign in | 10 failed attempts per email and 30 per network address in 15 minutes. The sentence is: Too many sign-in attempts. Try again in 15 minutes. |
| Password reset | 3 requests per email and 10 per network address in an hour. The sentence is: Too many reset requests. Check your inbox, or try again in an hour. Over the daily mail budget, the sentence is: We could not send the email right now. Try again later. |
| New accounts | 5 per hour and 20 per day per network address. The sentence is: Too many new accounts from this network. Try again later. |
| New keys | 20 per account per day. The sentence is: You have created many keys today. Try again tomorrow. |
| Printed labels | 50,000 labels per account per day. The sentence is: You have reached today's limit of 50,000 labels. |
A limited request is HTTP 429 with Retry-After in seconds and Cache-Control: no-store. It also carries RateLimit-Policy and RateLimit from the IETF draft, for example RateLimit-Policy: "render";q=20000;w=67 and RateLimit: "render";r=0;t=67. A successful upload carries the same two fields. Wait for Retry-After before you try again.
Size limits
These sentences are exact. A body over the limit is refused before it is read. A form error is HTTP 400. A body over the limit is HTTP 413. An address over the limit is HTTP 414.
| What | Sentence |
|---|---|
| File upload | This file is larger than 5 MB. Split it into smaller files. |
| Batch body | This request is larger than 2 MB. Send fewer items per request. |
| Form body | This form is too large. |
| Address | This address is too long. |
| CSV columns | A CSV can contain at most 100 columns. |
| CSV cell | Row {n} has a cell longer than 4,096 characters. |
| CSV rows | A CSV can contain at most 10,000 rows. |
| Batch items | A request can contain at most 10,000 items. |
| Label job | A label job can contain at most 10,000 labels. |
| Processing time | This file took too long to process. Split it into smaller files. |
Account and sign-in
These apply to the Account pages, not to API calls.
| What | Sentence |
|---|---|
| Enter a valid email address. | |
| Password | Use between 8 and 256 characters for the password. |
| Key name | Use at most 100 characters for the key name. |
| Active keys | You have 50 active keys. Revoke one before you create another. |
| We could not send the email right now. Try again later. |
One account keeps 20 sessions. A new sign-in deletes the oldest. The server closes a slow connection after 10 seconds for headers, 30 seconds for the whole request, and 5 seconds of idle keep-alive. The process keeps at most 10,000 successful responses and at most 256 MB of them, and drops the oldest. Those four have no sentence.
Retries
| Status | What to do |
|---|---|
| 429 | Wait the number of seconds in Retry-After, then send the same request again. Do not retry sooner. |
| 503 on a file or label job | The job took too long. Split the file into smaller files and send them one after another. |
| 413 or 414 | The body or the address is too large. Send fewer items per request, or a smaller file. |
| 400 | Do not retry. Fix the value or the request, using the code and the sentence. |
| 401 or 403 | Do not retry. Use a working key of the right type. |
| 500 | Retry once after a few seconds. If it fails again, contact us with the time of the request. |
Uploads run at most two at a time per account. Send files one after another rather than in parallel.
Caching and CORS
Successful JSON and PNG responses with k in the address use Cache-Control: public, max-age=31536000, immutable. Without k, they are private, no-store. Errors, the batch, the CSV, and formula failures use no-store. This process also keeps the last 10,000 successful responses. GET https://beepable.app/api lists the paths, and GET https://beepable.app/openapi.json describes them.
A browser on another origin can call these routes. Access-Control-Allow-Origin is *. An OPTIONS preflight is HTTP 204.
Stability
The routes, parameters, response fields, and error codes on this page are stable. New fields and new routes may be added, so ignore fields you do not know. A field, a route, or an error code will not be removed or renamed without notice on this page and in /openapi.json, whose version number changes with every published change. The same notice applies to an error sentence. In your code, branch on the error code and show the sentence to people.
Questions
Do I need an API key?
Yes, for a barcode, a batch, or a CSV. Sign in and create a key. A picture key draws pictures in a sheet. Put it in Z1. The value is still part of the image address, so do not send a private URL.
Which key goes where?
A picture key goes in the k parameter of an image address. An All routes key goes in Authorization: Bearer, from your own server. Never put an All routes key in an image address, because that address is public.
What image format comes back?
PNG. There is no SVG on these routes. A successful picture with k in the address uses Cache-Control public, max-age=31536000, immutable.
Does a bad check digit still return a barcode?
No. The JSON error code is bad_check_digit and the sentence is: Check digit is invalid for this GTIN. Name code128 to encode it anyway.