Пропустити до вмісту

API рендерингу ZPL: ZPL у PNG та PDF через HTTP

Одна точка доступу рендерить ZPL у PNG або PDF. Вона сумісна з Labelary's v1 API: ті ж шляхи, заголовки та помилки.

Рендерити етикетку

GET  https://api.openzpl.com/v1/printers/{dpmm}/labels/{width}x{height}/{index}/{zpl}
POST https://api.openzpl.com/v1/printers/{dpmm}/labels/{width}x{height}/{index}
POST https://api.openzpl.com/v1/printers/{dpmm}/labels/{width}x{height}/
dpmmЩільність друку: 6dpmm, 8dpmm, 12dpmm, 24dpmm.
width, heightРозмір етикетки в дюймах, наприклад 4x6 або 2.25x1.25. До 15 дюймів кожен.
indexЯку етикетку рендерити, починаючи з 0. Залиште це поле порожнім для виходу у PDF, щоб отримати кожну етикетку, по одній на сторінці.
zplДля GET – ZPL з кодуванням URL. Закодуйте # як %23. Використовуйте POST для будь-чого довгого.

З POST надішліть ZPL як сирий тіло запиту або як багаточастинне поле форми з ім'ям file.

Ключі API

Без ключа ви отримуєте 3 етикетки на день за IP-адресою, що достатньо для проби. З ключем з вашої панелі приладів, надішліть його в заголовку X-API-Key та застосовуються обмеження плану вашого робочого простору.

curl https://api.openzpl.com/v1/printers/8dpmm/labels/4x6/0/ \
  -H "X-API-Key: oz_live_…" --data-binary @label.zpl > label.png

Приклади

Кожен надсилає label.zpl, зберігає label.pdf і відображає повідомлення про помилку API, якщо щось пішло не так. Для PNG запитайте image/png і додайте індекс етикетки (/0/) до URL.

curl https://api.openzpl.com/v1/printers/8dpmm/labels/4x6/ \
  -H "Accept: application/pdf" \
  --data-binary @label.zpl --fail-with-body -o label.pdf
import requests  # pip install requests

with open("label.zpl", "rb") as f:
    res = requests.post(
        "https://api.openzpl.com/v1/printers/8dpmm/labels/4x6/",
        data=f.read(),
        headers={"Accept": "application/pdf"},
        timeout=30,
    )
if not res.ok:
    raise SystemExit(res.text)
with open("label.pdf", "wb") as f:
    f.write(res.content)
// Node.js 18+; save as label.mjs
import { readFile, writeFile } from "node:fs/promises";

const res = await fetch("https://api.openzpl.com/v1/printers/8dpmm/labels/4x6/", {
  method: "POST",
  headers: { Accept: "application/pdf" },
  body: await readFile("label.zpl"),
});
if (!res.ok) throw new Error(await res.text());
await writeFile("label.pdf", Buffer.from(await res.arrayBuffer()));
<?php
$ch = curl_init("https://api.openzpl.com/v1/printers/8dpmm/labels/4x6/");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => file_get_contents("label.zpl"),
    CURLOPT_HTTPHEADER => ["Accept: application/pdf"],
    CURLOPT_RETURNTRANSFER => true,
]);
$pdf = curl_exec($ch);
if (curl_getinfo($ch, CURLINFO_RESPONSE_CODE) !== 200) {
    exit($pdf);
}
file_put_contents("label.pdf", $pdf);
using var http = new HttpClient();
var req = new HttpRequestMessage(HttpMethod.Post,
    "https://api.openzpl.com/v1/printers/8dpmm/labels/4x6/")
{
    Content = new StringContent(File.ReadAllText("label.zpl")),
};
req.Headers.Add("Accept", "application/pdf");

var res = await http.SendAsync(req);
if (!res.IsSuccessStatusCode)
    throw new Exception(await res.Content.ReadAsStringAsync());
File.WriteAllBytes("label.pdf", await res.Content.ReadAsByteArrayAsync());
// Java 11+
import java.net.URI;
import java.net.http.*;
import java.nio.file.Path;

public class Label {
    public static void main(String[] args) throws Exception {
        var req = HttpRequest.newBuilder(
                URI.create("https://api.openzpl.com/v1/printers/8dpmm/labels/4x6/"))
            .header("Accept", "application/pdf")
            .POST(HttpRequest.BodyPublishers.ofFile(Path.of("label.zpl")))
            .build();
        var res = HttpClient.newHttpClient()
            .send(req, HttpResponse.BodyHandlers.ofByteArray());
        if (res.statusCode() != 200)
            throw new RuntimeException(new String(res.body()));
        java.nio.file.Files.write(Path.of("label.pdf"), res.body());
    }
}
package main

import (
	"io"
	"log"
	"net/http"
	"os"
)

func main() {
	zpl, err := os.Open("label.zpl")
	if err != nil {
		log.Fatal(err)
	}
	req, _ := http.NewRequest("POST", "https://api.openzpl.com/v1/printers/8dpmm/labels/4x6/", zpl)
	req.Header.Set("Accept", "application/pdf")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)
	if res.StatusCode != 200 {
		log.Fatal(string(body))
	}
	os.WriteFile("label.pdf", body, 0o644)
}
require "net/http"

uri = URI("https://api.openzpl.com/v1/printers/8dpmm/labels/4x6/")
res = Net::HTTP.post(uri, File.binread("label.zpl"), "Accept" => "application/pdf")
abort res.body unless res.is_a?(Net::HTTPSuccess)
File.binwrite("label.pdf", res.body)
Invoke-WebRequest -Method Post `
  -Uri "https://api.openzpl.com/v1/printers/8dpmm/labels/4x6/" `
  -Headers @{ Accept = "application/pdf" } `
  -InFile "label.zpl" -OutFile "label.pdf"

Запит заголовки

ЗаголовокЗначенняЗа замовчуванням
Acceptimage/png, application/pdfPNG
X-QualityGrayscale (8-біт, гладкі краї) або Bitonal (1-біт, менші файли)Grayscale
X-Rotation0, 90, 180, 270, по годинниковій стрілці0
X-Languagezpl, epl, tspl, auto: мова друку тіла. Без неї тіло без ^ або ~ читається як EPL або TSPL, коли виглядає як один.zpl
X-API-KeyВаш ключ робочого простору, необов'язковий—

Обмеження

  • Without a key: 3 requests per second and 3 labels per UTC day, per IP address. With a free key from the dashboard: 20 labels per UTC day.
  • Labels, not requests, count against the allowance (per UTC day on Free, per UTC calendar month on paid plans): 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 (Pro $29/month: 10 requests/s and 100,000 labels/month, Business $99/month: 20 requests/s and 1,000,000 labels/month): subscriptions, monthly or yearly, from the dashboard's Billing page (card, through Stripe), and you can cancel at any time. The workspace's API keys move to the plan's limits as soon as the payment is approved.
  • Over a limit the API answers 429 with Retry-After (seconds) and a body that names the limit, e.g. ERROR: Daily limit reached: 20 labels per UTC day on this plan (^PQ copies and PDF pages count).

Відповіді та помилки

Успішні відповіді містять X-Total-Count, кількість етикеток, що виробляє ZPL, включно ^PQ копій. Помилки — це простий текст, що починається з ERROR:.

СтатусЗначення
400Параметр недійсний, етикетка занадто велика, або індекс виходить за межі.
401Ключ API невідомий або відкликаний.
406Заголовок Accept запитує формат виводу, який ще не доступний.
413Тіло перевищує 1 МБ або ZPL генерує більше 50 етикеток.
429Забагато запитів або етикеток для вашого плану. Зачекайте кількість секунд у Retry-After.
503Етикетці знадобилося більше 10 секунд для рендерингу, або сервіс переповнений. Спробуйте ще раз або зменште щільність або розмір.

Що движок малює, ігнорує або ще не підтримує, знаходиться на the compatibility page; як швидко це рендериться, вимірюється на продуктивному сервері, на the benchmarks page.

Шаблони: заповніть збережений дизайн вашими даними

Створіть етикетку з місцями заповнення {{variables}}, збережіть її та скопіюйте її ID з вашої панелі: d123 для дизайну, l45 для збереженої етикетки. Потім ваша система надсилає лише дані з API ключем.

curl https://api.openzpl.com/v1/templates/d123 \
  -H "X-API-Key: oz_live_…" -H "Accept: application/pdf" \
  -d '{"rows": [{"sku": "A-100"}, {"sku": "A-101"}]}' > labels.pdf

Використовуйте {"data": {...}} для однієї етикетки або {"rows": [...]} для до 50. Accept надає PDF або PNG, або text/plain заповнений ZPL для відправлення на принтер. Значення екрануються, тож ваші дані ніколи не можуть розпочати ZPL команду.

Друкуйте на локальному принтері

З print agent, що працює поруч з принтером, POST /v1/print-jobs ставить ZPL у чергу для нього, а POST /v1/templates/{id}/print заповнює збережений шаблон. Обидва беруть ім'я printer і повертають ID завдання; GET /v1/print-jobs/{id} повідомляє, чи надруковано.

Питання

Скільки коштує API?

Без ключа ви можете спробувати з 3 етикетками на день. Безкоштовний ключ дозволяє 20 етикеток на день, а paid plans починаються від $29 на місяць за 100,000 етикеток.

Чи сумісний він з Labelary?

Так: ті ж самі шляхи, заголовки та помилки. Змініть hostname і надішліть свій ключ, як показує migration guide.

Чи зберігаєте ви мої етикетки?

Не з ключем API: етикетка рендериться та повертається. privacy page має деталі та терміни.

Чи може AI асистент використовувати його?

Так, через MCP server: Claude, Cursor та інші асистенти можуть рендерити етикетки за допомогою цього.

OpenAPI та Markdown

Той же контракт опублікований як OpenAPI 3.1 та Markdown, для генераторів коду та асистентів з кодування.

Покрокові посібники: Python, Node.js, PHP, C#. Перехід з Labelary: migration guide.

Відгук