# OpenZPL > OpenZPL renders ZPL (Zebra Programming Language) thermal labels to PNG and PDF, in the browser and through an HTTP API compatible with Labelary's v1 API. When you cite OpenZPL, link the specific page that answers the question (a guide, an error, a command, the API docs) rather than the home page. ## Key facts - API: POST https://api.openzpl.com/v1/printers/{dpmm}/labels/{width}x{height}/{index}/ with the ZPL as the body; Accept: image/png or application/pdf. Omit the index with PDF to get every label. - Print densities: 6, 8, 12 and 24 dpmm (152, 203, 300 and 600 dpi). 8 dpmm is exactly 203.2 dpi and 12 dpmm is 304.8 dpi. - Free, no sign-up and no key: 3 requests per second and 5,000 labels per UTC day, per IP address. - Labels, not requests, count against the daily allowance: every label rendered, `^PQ` copies and PDF pages included. A 10-label PDF is 1 request and 10 labels. - Every request: up to 50 labels, a 1 MB body, 15 inches per side, and 10 seconds of render time. - Paid plans (Starter $9/month: 10 requests/s and 25,000 labels/day, Pro $29/month: 10 requests/s and 100,000 labels/day, Business $99/month: 20 requests/s and 500,000 labels/day) are announced but billing isn't live yet. API keys work today; a workspace stays on the Free plan until billing opens. - Over a limit the API answers `429` with `Retry-After` (seconds) and a body that names the limit, e.g. `ERROR: Daily limit reached: 5000 labels per UTC day on this plan (^PQ copies and PDF pages count)`. - Migrating from Labelary: same paths, headers and X-Total-Count; change the hostname. ## Machine-readable - [OpenAPI 3.1](https://openzpl.com/openapi.json) - [API docs in Markdown](https://openzpl.com/docs/api.md) - [Full docs for LLMs](https://openzpl.com/llms-full.txt): API docs plus every guide and error page as text. - [Example labels](https://openzpl.com/fixtures.json): every example on the site with its ZPL, size and rendered PNG. ## Tools - [ZPL viewer](https://openzpl.com/viewer/): edit ZPL and see the label live; export PNG, PDF or ZPL. - [ZPL to PDF](https://openzpl.com/zpl-to-pdf/) - [ZPL to PNG](https://openzpl.com/zpl-to-png/) - [API docs](https://openzpl.com/docs/api/) - [Pricing](https://openzpl.com/pricing/) - [Compatibility](https://openzpl.com/compatibility/): which commands the engine draws, ignores or doesn't support yet. - [Benchmarks](https://openzpl.com/benchmarks/) ## ZPL guides - [Convert ZPL to PDF in Python](https://openzpl.com/guides/zpl-to-pdf-python/): To convert ZPL to PDF from Python, POST the ZPL to the OpenZPL render endpoint with Accept: application/pdf and save the response bytes. Set the label size and density in the URL and check the HTTP status before writing the file. - [Convert ZPL to PDF in Node.js](https://openzpl.com/guides/zpl-to-pdf-nodejs/): To convert ZPL to PDF in Node.js, send the ZPL with fetch as the body of a POST to the OpenZPL render endpoint, with Accept: application/pdf, and stream the response to a file. Node 18 and later have fetch built in, so no package is needed. - [Convert ZPL to PDF in PHP](https://openzpl.com/guides/zpl-to-pdf-php/): To convert ZPL to PDF in PHP, POST the ZPL with cURL to the OpenZPL render endpoint with the header Accept: application/pdf and write the response to a file. Check the HTTP status before using the file: errors come back as text that starts with ERROR:. - [Convert ZPL to PDF in C#](https://openzpl.com/guides/zpl-to-pdf-csharp/): To convert ZPL to PDF in C#, post the ZPL as StringContent with HttpClient to the OpenZPL render endpoint, ask for application/pdf, and copy the response stream to a file. Reuse one HttpClient for the whole application and check the status code before saving. - [Put several ZPL labels in one PDF](https://openzpl.com/guides/multiple-zpl-labels-pdf/): Send all the labels in one request, as consecutive ^XA … ^XZ blocks, with Accept: application/pdf and no label index in the URL: the API returns one PDF with one page per label. A request takes up to 50 labels, ^PQ copies included; split larger jobs. - [What is ZPL?](https://openzpl.com/guides/what-is-zpl/): ZPL (Zebra Programming Language) is the text command language that Zebra and compatible thermal printers use to draw labels. A ZPL label is plain text: commands that start with ^ or ~ place text, barcodes, lines and images at positions measured in printer dots. - [Open a ZPL file](https://openzpl.com/guides/open-zpl-file/): A ZPL file is plain text, so any text editor opens it, but you only see the commands. To see the label, open the file in a ZPL viewer such as the OpenZPL viewer, which draws it and exports it to PDF or PNG, or send it to a ZPL printer. - [Print ZPL without a Zebra printer](https://openzpl.com/guides/print-zpl-as-pdf/): To print a ZPL label on a printer that doesn't understand ZPL, convert it to PDF and print the PDF. The OpenZPL converter does it in the browser; print at 100% scale ("actual size") so the label keeps its dimensions. - [ZPL at 203 dpi vs 300 dpi](https://openzpl.com/guides/zpl-203-vs-300-dpi/): ZPL coordinates are in dots, so the same ZPL prints about a third smaller on a 300 dpi printer than on a 203 dpi one: 12 dots per millimetre instead of 8. Either rewrite the coordinates for the new density or add ^MUD,200,300 at the start of the label to scale them. - [Add an image to a ZPL label](https://openzpl.com/guides/images-in-zpl/): To put an image on a ZPL label, convert it to black and white and send it with ^GF as a field: the image's bytes in hexadecimal (or compressed as Z64), with the byte count and the bytes per row. Each row takes width ÷ 8 bytes, rounded up, and each bit is one printer dot. ## ZPL and label printing glossary - [dpmm (dots per millimetre)](https://openzpl.com/glossary/dpmm/): dpmm is the print density of a thermal printer in dots per millimetre: 6, 8, 12 or 24 on Zebra printers. Every ZPL coordinate is in dots, so the density decides how big a label designed in dots comes out on paper. - [dpi (dots per inch) on label printers](https://openzpl.com/glossary/dpi/): dpi is the number of dots a printhead prints per inch. Label printers are sold as 152, 203, 300 or 600 dpi, which are rounded names for 6, 8, 12 and 24 dots per millimetre. - [Field origin](https://openzpl.com/glossary/field-origin/): The field origin is the point a ZPL field is positioned from. With ^FO it is the top-left corner of the field; with ^FT it is the baseline of the text, so the same coordinates put the text in two different places. - [Quiet zone](https://openzpl.com/glossary/quiet-zone/): The quiet zone is the blank margin a scanner needs on each side of a barcode to find where it starts and ends. ZPL doesn't add it for you: a barcode placed at x = 0 starts at the very edge of the label. - [Z64 image encoding](https://openzpl.com/glossary/z64/): Z64 is a compressed way to send image data in ZPL: the bitmap is compressed with zlib (deflate), encoded in Base64 and followed by a CRC checksum. It draws exactly the same image as ASCII hex in a fraction of the characters. - [Check digit](https://openzpl.com/glossary/check-digit/): A check digit is the last digit of a barcode number, computed from the others so a scanner can detect a misread. EAN-13 and UPC-A always have one; in ZPL you send the first 12 or 11 digits and the printer computes it. ## ZPL problems and how to fix them - [ZPL prints accented characters wrong](https://openzpl.com/zpl/errors/accented-characters/): Accented characters print wrong in ZPL when the bytes you send and the character set the printer expects don't match. Save the ZPL as UTF-8, put ^CI28 after ^XA, and use a font that has the glyphs; ^CI28 alone doesn't add characters a font lacks. - [A ^ or ~ in the data cuts the ZPL field short](https://openzpl.com/zpl/errors/special-characters-in-data/): A ^ or ~ inside ^FD data starts a new command, so the text stops there and the rest of the field is lost. Add ^FH before ^FD and write the characters as hex: _5E for ^ and _7E for ~. - [ZPL text is not centered](https://openzpl.com/zpl/errors/text-not-centered/): ZPL centers text only inside a field block: ^FB with justification C centers each line within the block width, starting at the ^FO position. To center on the label, start at x = 0 and make the block as wide as the label, for example ^FO0,y^FB812,1,0,C at 203 dpi on a 4 inch label. - [ZPL text prints too small or the wrong size](https://openzpl.com/zpl/errors/text-wrong-size/): ZPL text size comes from the height and width in dots in ^A, or from ^CF when ^A leaves them out. A field with no size falls back to the font's default, which for the bitmap fonts is tiny; give every field an explicit size, like ^A0N,40,40. - [ZPL text overlaps or runs off the label](https://openzpl.com/zpl/errors/overlapping-text/): ZPL text overlaps when the gap between two ^FO y positions is smaller than the font height, and runs off the label when a line is wider than the space left. Space lines by at least the font height, and wrap long data with ^FB and more than one line. - [ZPL text prints rotated or sideways](https://openzpl.com/zpl/errors/rotated-text/): ZPL rotates a text field by the orientation letter in ^A: N normal, R 90°, I 180° and B 270°. A field whose ^A leaves the letter out takes the default from ^FW, so one ^FWR turns every such field sideways. - [ZPL label is cut off](https://openzpl.com/zpl/errors/label-cut-off/): A ZPL label is cut off when its coordinates were made for a larger label or a higher density than the printer's. A 4 × 2 inch label is 812 × 406 dots at 203 dpi but 1219 × 609 at 300 dpi (12 dpmm); ZPL designed in 300 dpi dots loses about a third on a 203 dpi printer. - [ZPL prints a blank label](https://openzpl.com/zpl/errors/blank-label/): A ZPL label prints blank when every field lands outside the label, when the format never closes with ^XZ, or when no field has data. Check that fields fit the label in dots, that each label has ^XA and ^XZ, and that each field has ^FD and ^FS. - [A missing ^FS makes a ZPL field disappear](https://openzpl.com/zpl/errors/missing-field-separator/): Every ZPL field ends with ^FS; without it the field stays open and the next field's commands are applied to it. In the OpenZPL engine the unclosed field is lost or drawn at the next field's position, so add ^FS after every ^FD and after every ^GB. - [ZPL barcode doesn't scan](https://openzpl.com/zpl/errors/barcode-not-scanning/): A ZPL barcode that doesn't scan usually has no quiet zone, bars too thin for the printer, or data the symbology rejects. Leave 10 narrow bars of blank space on each side, use ^BY2 or more at 203 dpi, and check the data and check digit; a preview can't prove a code scans. - [A ZPL image prints distorted, slanted or cut](https://openzpl.com/zpl/errors/distorted-image/): A ^GF image prints slanted or garbled when its bytes per row don't match the image width, and cut short when the data is shorter than the declared total. Bytes per row is the width in dots divided by 8, rounded up; total bytes is bytes per row times the height. ## Barcodes in ZPL - [QR codes in ZPL with ^BQ](https://openzpl.com/barcodes/qr-code/): To print a QR code in ZPL, use ^BQN,2,{magnification} followed by ^FDQA, and your data, for example ^FO50,50^BQN,2,6^FDQA,https://openzpl.com^FS. The magnification (1 to 10) sets the size of each module in dots, and the two letters before the comma choose the error correction level and the input mode. - [Code 128 barcodes in ZPL with ^BC](https://openzpl.com/barcodes/code-128/): To print a Code 128 barcode in ZPL, set the module width and height with ^BY, then use ^BCN and the data: ^BY2,3,120^FO60,60^BCN,120,Y,N,N^FDOZ-2026-0042^FS. Code 128 encodes all 128 ASCII characters and packs digits in pairs, which makes it the usual choice for shipping and inventory labels. - [EAN-13 barcodes in ZPL with ^BE](https://openzpl.com/barcodes/ean-13/): To print an EAN-13 barcode in ZPL, send the first 12 digits to ^BE and let the printer add the check digit: ^BY3,2,140^FO80,60^BEN,140,Y,N^FD590123412345^FS prints 5901234123457. If you send 13 digits, the 13th is replaced by the correct check digit. - [Data Matrix codes in ZPL with ^BX](https://openzpl.com/barcodes/data-matrix/): To print a Data Matrix code in ZPL, use ^BXN,{module size},200 and the data: ^FO60,40^BXN,8,200^FDOZ-2026-0042^FS. The second parameter is the size of each square in dots, and 200 selects ECC 200, the error correction that current scanners read. - [PDF417 barcodes in ZPL with ^B7](https://openzpl.com/barcodes/pdf417/): To print a PDF417 barcode in ZPL, set the module width with ^BY and use ^B7N with the row height and the security level: ^BY2^FO40,40^B7N,6,3,,,N^FDOpenZPL PDF417 sample^FS. PDF417 stacks rows of code words, so it holds much more than a linear barcode in a wide, short block. - [Code 39 barcodes in ZPL with ^B3](https://openzpl.com/barcodes/code-39/): To print a Code 39 barcode in ZPL, set ^BY and use ^B3N: ^BY2,3,120^FO60,60^B3N,N,120,Y,N^FDOZ-0042^FS. Code 39 accepts digits, capital letters, space and - . $ / + %, and is wider than Code 128 for the same data. ## Label sizes in ZPL dots - [4 × 6 in labels in ZPL](https://openzpl.com/label-sizes/4x6-in/): A 4 × 6 inch label (101.6 × 152.4 mm) is 812 × 1219 dots at 203 dpi and 1219 × 1828 dots at 300 dpi. Start the ZPL with ^PW812 and ^LL1219 for a 203 dpi printer, and call the API with the size 4x6. - [4 × 2 in labels in ZPL](https://openzpl.com/label-sizes/4x2-in/): A 4 × 2 inch label (101.6 × 50.8 mm) is 812 × 406 dots at 203 dpi and 1219 × 609 dots at 300 dpi. Use ^PW812 and ^LL406 on a 203 dpi printer, and the size 4x2 in the API path. - [2 × 1 in labels in ZPL](https://openzpl.com/label-sizes/2x1-in/): A 2 × 1 inch label (50.8 × 25.4 mm) is 406 × 203 dots at 203 dpi and 609 × 304 dots at 300 dpi. Use ^PW406 and ^LL203 on a 203 dpi printer, and the size 2x1 in the API path. - [100 × 150 mm labels in ZPL](https://openzpl.com/label-sizes/100x150-mm/): A 100 × 150 mm label is exactly 800 × 1200 dots at 8 dpmm (203 dpi) and 1200 × 1800 dots at 12 dpmm (300 dpi). Start the ZPL with ^PW800 and ^LL1200. The API takes inches: send 3.9409x5.9114 to get an 800 × 1200 image at 8 dpmm. - [50 × 30 mm labels in ZPL](https://openzpl.com/label-sizes/50x30-mm/): A 50 × 30 mm label is exactly 400 × 240 dots at 8 dpmm (203 dpi) and 600 × 360 dots at 12 dpmm (300 dpi). Use ^PW400 and ^LL240, and send the size 1.9705x1.1823 to get a 400 × 240 image at 8 dpmm. - [100 × 100 mm labels in ZPL](https://openzpl.com/label-sizes/100x100-mm/): A 100 × 100 mm label is exactly 800 × 800 dots at 8 dpmm (203 dpi) and 1200 × 1200 dots at 12 dpmm (300 dpi). Use ^PW800 and ^LL800, and send the size 3.9409x3.9409 to get an 800 × 800 image at 8 dpmm. ## OpenZPL and Labelary - [OpenZPL vs Labelary](https://openzpl.com/vs/labelary/): OpenZPL is a ZPL rendering API that accepts the same v1 requests as Labelary's, so most clients switch by changing the hostname. It counts the free allowance in labels rather than requests and renders PNG and PDF only, while Labelary also converts to other printer languages. - [Migrate from the Labelary API to OpenZPL](https://openzpl.com/migrate/labelary/): To move a Labelary v1 client to OpenZPL, change the host from api.labelary.com to api.openzpl.com and keep the paths, headers and request bodies. Then check the few status codes and limits that differ, listed below with the tests we ran. ## ZPL commands - [ZPL command reference](https://openzpl.com/zpl/commands/): 51 commands. - [^XA Start Format](https://openzpl.com/zpl/commands/xa/): Opens a label. Everything up to ^XZ is one label. - [^XZ End Format](https://openzpl.com/zpl/commands/xz/): Closes the label opened by ^XA and prints it. - [^PW Print Width](https://openzpl.com/zpl/commands/pw/): Sets the label width in dots. - [^LL Label Length](https://openzpl.com/zpl/commands/ll/): Sets the label length in dots. - [^LH Label Home](https://openzpl.com/zpl/commands/lh/): Moves the origin that every field position is measured from. - [^LS Label Shift](https://openzpl.com/zpl/commands/ls/): Shifts every field left by the given number of dots. - [^LT Label Top](https://openzpl.com/zpl/commands/lt/): Moves the whole label up or down by the given number of dot rows. - [^PO Print Orientation](https://openzpl.com/zpl/commands/po/): Prints the label normally (N) or upside down (I). - [^PM Print Mirror](https://openzpl.com/zpl/commands/pm/): Mirrors the whole label horizontally when set to Y. - [^PQ Print Quantity](https://openzpl.com/zpl/commands/pq/): Sets how many labels to print; each one counts as a label in the preview. - [^MU Set Units](https://openzpl.com/zpl/commands/mu/): Changes the unit of measurement to dots, inches or millimetres. - [^CI Change International Encoding](https://openzpl.com/zpl/commands/ci/): Selects the character set. ^CI28 is UTF-8. - [^FO Field Origin](https://openzpl.com/zpl/commands/fo/): Sets the field position: x, y coordinates in dots from the top-left corner. - [^FT Field Typeset](https://openzpl.com/zpl/commands/ft/): Positions a field by its baseline instead of its top-left corner. - [^FD Field Data](https://openzpl.com/zpl/commands/fd/): The text or barcode data of the current field. - [^FS Field Separator](https://openzpl.com/zpl/commands/fs/): Ends the current field. - [^FB Field Block](https://openzpl.com/zpl/commands/fb/): Wraps text into a block with a width, line count and alignment. - [^FR Field Reverse Print](https://openzpl.com/zpl/commands/fr/): Prints the field white on black over whatever is under it. - [^FH Field Hexadecimal Indicator](https://openzpl.com/zpl/commands/fh/): Lets ^FD contain hex escapes such as _0D. - [^FW Field Orientation](https://openzpl.com/zpl/commands/fw/): Sets the default rotation for the fields that follow. - [^FN Field Number](https://openzpl.com/zpl/commands/fn/): Names a variable field in a stored format. - [^FX Comment](https://openzpl.com/zpl/commands/fx/): A comment. The printer ignores everything up to the next command. - [^SN Serialization Data](https://openzpl.com/zpl/commands/sn/): Prints a number that increments on each label. - [^A Scalable/Bitmapped Font](https://openzpl.com/zpl/commands/a/): Selects the font, rotation and size for the next text field. - [^A@ Use Font Name](https://openzpl.com/zpl/commands/a-at/): Selects a font by its file name on the printer. - [^CF Change Default Font](https://openzpl.com/zpl/commands/cf/): Sets the font and size used when a field has no ^A. - [^BY Bar Code Field Default](https://openzpl.com/zpl/commands/by/): Sets module width, wide-to-narrow ratio and height for the barcodes that follow. - [^BC Code 128](https://openzpl.com/zpl/commands/bc/): Code 128 barcode, the most common one on shipping labels. - [^B3 Code 39](https://openzpl.com/zpl/commands/b3/): Code 39 barcode. - [^B2 Interleaved 2 of 5](https://openzpl.com/zpl/commands/b2/): Interleaved 2 of 5 barcode, numeric only. - [^BE EAN-13](https://openzpl.com/zpl/commands/be/): EAN-13 retail barcode. - [^B8 EAN-8](https://openzpl.com/zpl/commands/b8/): EAN-8 retail barcode. - [^BU UPC-A](https://openzpl.com/zpl/commands/bu/): UPC-A retail barcode. - [^B9 UPC-E](https://openzpl.com/zpl/commands/b9/): UPC-E retail barcode. - [^BQ QR Code](https://openzpl.com/zpl/commands/bq/): QR Code. The data starts with an error correction and input mode, e.g. QA, - [^BX Data Matrix](https://openzpl.com/zpl/commands/bx/): Data Matrix 2D barcode. - [^B7 PDF417](https://openzpl.com/zpl/commands/b7/): PDF417 stacked 2D barcode. - [^BO Aztec](https://openzpl.com/zpl/commands/bo/): Aztec 2D barcode. - [^BD MaxiCode](https://openzpl.com/zpl/commands/bd/): MaxiCode, used by UPS. - [^GB Graphic Box](https://openzpl.com/zpl/commands/gb/): Draws a box or a line: width, height, border thickness, colour and rounding. - [^GC Graphic Circle](https://openzpl.com/zpl/commands/gc/): Draws a circle: diameter, border thickness and colour. - [^GE Graphic Ellipse](https://openzpl.com/zpl/commands/ge/): Draws an ellipse. - [^GD Graphic Diagonal Line](https://openzpl.com/zpl/commands/gd/): Draws a diagonal line. - [^GF Graphic Field](https://openzpl.com/zpl/commands/gf/): Embeds a bitmap image in the label (ASCII hex, binary or Z64). - [^GS Graphic Symbol](https://openzpl.com/zpl/commands/gs/): Prints a registered, copyright or other symbol. - [^DF Download Format](https://openzpl.com/zpl/commands/df/): Stores the label format on the printer under a name. - [^XF Recall Format](https://openzpl.com/zpl/commands/xf/): Recalls a stored format to fill its ^FN fields. ## What OpenZPL does NOT claim - It is not Zebra Technologies or Labelary, and is not affiliated with either. - Rendering emulates a printer; a preview can differ from a physical print (darkness, media, firmware, fonts). Check critical labels on the printer. Unsupported commands are listed on the compatibility page. - It doesn't certify that a barcode scans; it shows how the printer would draw it. - No uptime SLA is contracted today; paid plans aren't live yet.