zpl.tools and Labelary
Response time, accepted page counts and response size, measured 2026-08-17
Labelary is a hosted service: api.labelary.com, the hosted rendering API. This page compares it with zpl.tools — api.zpl.tools, the Labelary-compatible surface. One client sends both requests over the same connection, so both sides carry the same network time.
Labelary documents two limits on a request: 50 labels, and a 1 MB request body.
Response time
- zpl.tools
- Labelary
Values behind this chart
| Engine | 1 p | 2 p | 3 p | 4 p | 5 p | 10 p | 15 p | 20 p | 25 p | 30 p | 35 p | 40 p | 45 p | 50 p | 75 p | 100 p | 150 p | 200 p | 300 p | 500 p |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| zpl.tools | 53 ms | 55 ms | 58 ms | 54 ms | 51 ms | 56 ms | 73 ms | 76 ms | 84 ms | 85 ms | 99 ms | 99 ms | 107 ms | 129 ms | 165 ms | 207 ms | 281 ms | 349 ms | 492 ms | 846 ms |
| Labelary | 110 ms | 111 ms | 113 ms | 124 ms | 117 ms | 139 ms | 252 ms | 270 ms | 283 ms | 292 ms | 364 ms | 319 ms | 374 ms | 400 ms | 0% | 0% | — | — | — | — |
On one page, zpl.tools answers in a median 53 ms and Labelary in 110 ms, a factor of 2.1. At 50 pages, the largest packet the benchmark sent to both engines, the medians are 129 ms and 400 ms. zpl.tools rendered 500 pages in a median 846 ms, and 500 pages is where the benchmark stopped — the renderer sets no page limit. Labelary rejects a request past 50 pages.
Accepted requests
Labelary rejected the request of 75 pages on the orderful style corpus, and every larger request. The chart draws the share of requests that returned 200. It drops from 100% to 0% at that page count.
- zpl.tools
- Labelary
Values behind this chart
| Engine | 1 p | 2 p | 3 p | 4 p | 5 p | 10 p | 15 p | 20 p | 25 p | 30 p | 35 p | 40 p | 45 p | 50 p | 75 p | 100 p | 150 p | 200 p | 300 p | 500 p |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| zpl.tools | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% |
| Labelary | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 0% | 0% | — | — | — | — |
zpl.tools accepted every page count the run measured, up to 500 pages.
Response size
- zpl.tools
- Labelary
Values behind this chart
| Engine | 1 p | 2 p | 3 p | 4 p | 5 p | 10 p | 15 p | 20 p | 25 p | 30 p | 35 p | 40 p | 45 p | 50 p | 75 p | 100 p | 150 p | 200 p | 300 p | 500 p |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| zpl.tools | 13.9 KB | 15.9 KB | 18.1 KB | 20.9 KB | 23.6 KB | 42.2 KB | 52.3 KB | 63.4 KB | 72.7 KB | 82.3 KB | 92.4 KB | 101.3 KB | 110.2 KB | 119.5 KB | 162.8 KB | 203.5 KB | 290.9 KB | 361.5 KB | 504.9 KB | 791.0 KB |
| Labelary | 17.4 KB | 19.7 KB | 22.0 KB | 25.8 KB | 28.3 KB | 53.5 KB | 67.1 KB | 79.4 KB | 90.6 KB | 101.3 KB | 112.4 KB | 121.8 KB | 132.4 KB | 143.1 KB | 0% | 0% | — | — | — | — |
A one-page PDF from zpl.tools is 13.9 KB. At 500 pages the same response is 791.0 KB: 56.8 times the size for 500 times the pages. The PDF writer stores one copy of a repeated page and references it, so a packet grows by its per-page content, not by a whole page. At 50 pages, the response is 119.5 KB against 143.1 KB from Labelary.
Corpora
Each corpus is one request body, repeated at every page count. A corpus reaches this page only where the run measured both engines on it.
Simple label
Labelary's own example label, duplicated per page. Every page carries the same label.
| Pages | zpl.tools p50 | zpl.tools p90 | zpl.tools p95 | Labelary p50 | Labelary p90 | Labelary p95 |
|---|---|---|---|---|---|---|
| 1 | 55 ms | 171 ms | 523 ms | 109 ms | 216 ms | 241 ms |
| 10 | 48 ms | 86 ms | 104 ms | 125 ms | 163 ms | 247 ms |
| 50 | 92 ms | 98 ms | 120 ms | 300 ms | 466 ms | 524 ms |
| 100 | 156 ms | 165 ms | 179 ms | rejected | rejected | rejected |
| 500 | 521 ms | 596 ms | 606 ms | — | — | — |
GS1 graphics label
A real retailer label with embedded ~DG graphics, duplicated per page. Every page carries the same label.
| Pages | zpl.tools p50 | zpl.tools p90 | zpl.tools p95 | Labelary p50 | Labelary p90 | Labelary p95 |
|---|---|---|---|---|---|---|
| 1 | 45 ms | 53 ms | 58 ms | 247 ms | 395 ms | 406 ms |
| 10 | 73 ms | 82 ms | 89 ms | 337 ms | 378 ms | 496 ms |
| 50 | 162 ms | 189 ms | 263 ms | rejected | rejected | rejected |
| 100 | 228 ms | 293 ms | 356 ms | rejected | rejected | rejected |
| 500 | 891 ms | 1,003 ms | 1,036 ms | — | — | — |
Orderful style
Orderful retail compliance labels, a different label on every page. Every page carries a different label, so no engine can reuse a page it already rendered.
| Pages | zpl.tools p50 | zpl.tools p90 | zpl.tools p95 | Labelary p50 | Labelary p90 | Labelary p95 |
|---|---|---|---|---|---|---|
| 1 | 53 ms | 56 ms | 56 ms | 110 ms | 149 ms | 179 ms |
| 10 | 56 ms | 65 ms | 65 ms | 139 ms | 229 ms | 363 ms |
| 50 | 129 ms | 136 ms | 137 ms | 400 ms | 495 ms | 635 ms |
| 100 | 207 ms | 209 ms | 211 ms | rejected | rejected | rejected |
| 500 | 846 ms | 962 ms | 965 ms | — | — | — |
SPS Style
The same retailer labels delivered as ~DG bitmap pages, the SPS Commerce way. Every page carries a different label, so no engine can reuse a page it already rendered.
| Pages | zpl.tools p50 | zpl.tools p90 | zpl.tools p95 | Labelary p50 | Labelary p90 | Labelary p95 |
|---|---|---|---|---|---|---|
| 1 | 71 ms | 79 ms | 82 ms | 338 ms | 374 ms | 467 ms |
| 10 | 155 ms | 189 ms | 202 ms | rejected | rejected | rejected |
| 50 | 536 ms | 775 ms | 797 ms | rejected | rejected | rejected |
| 100 | 997 ms | 1,399 ms | 1,403 ms | rejected | rejected | rejected |
Method
k6 drives every point, one request at a time, 10 iterations per point, 5 past 150 pages. A one-page point carries 10 samples.
Client: k6, one request at a time. Both hosted engines measured in the same session from a Hetzner ccx13 (dedicated vCPU) in the fsn1 datacenter, Germany — network RTT included, the same for both sides. Local engines measured on a MacBook Pro (Apple Silicon), no network crossed; the zpl.tools, zebrash, BinaryKits and Neodynamic summaries carry over from the 2026-08-09 run, because no code in the render path changed between them. labelize was added on 2026-08-17 and measured on the same MacBook, at one page only — see below.
Measured 2026-08-17. The harness is benchmarks/run.sh, and its header names the local servers each engine needs. To repeat the run:
./benchmarks/run.sh
node scripts/distill-benchmarks.mjs
node scripts/generate-performance-pages.mjsThe first command measures, the second writes website/lib/performance-data.json, and the third writes this page from it.