Download a Label
/api/v1/labels/{labelId}/download
Outcome
After successful Label purchase,
retain postageLabel.id and use it with
Download Label.
You will handle a successful response as binary bytes, store or forward those
bytes according to your application's needs, and distinguish a binary 200
from a bounded 202 or documented error.
When to use
Use this guide after a successful Label-purchase response provides
postageLabel.id. Download Label is the documented operation for retrieving
the Label file.
When not to use
This guide does not define Label purchase, tracking, direct asset URL retrieval, polling schedules, readiness prediction, filename rules, cache lifetime, provider storage behavior, financial reconciliation, or cancellation.
Prerequisites
Have:
- a valid bearer credential for the intended Account;
- the retained
postageLabel.idfrom a successful Label-purchase response; - a client capable of handling binary HTTP bodies.
X-SubAccount-Id is optional in the current operation contract, but the
current metadata states that it has no effect on Download Label. It is not a
required download input.
Conceptual explanation
Download Label does not return the standard JSON envelope on success. Its
required path input is labelId; the optional format query parameter selects
the documented format when supplied. The API defines 1 for PDF and 2, 3, and
4 for ZPL at 300, 203, and 600 DPI. Each ZPL resolution is a separate stored
file: a resolution is either returned exactly or reported as unavailable, never
substituted with another one or with the PDF. When format is omitted, the
current contract uses the Account's preferred LabelPrintingTechnology.
End-to-end flow
successful Label purchase
-> retain postageLabel.id
-> call DownloadLabel
-> inspect HTTP status and Content-Type
-> on 200, consume binary body
-> on 202, treat the asset as pending/still generating
-> on documented error, inspect the actual response
Request example
labelId is required in the path. format is optional and must use one of the
documented values when supplied.
GET <API_BASE_URL>/api/v1/labels/<LABEL_ID>/download?format=<FORMAT>
Authorization: Bearer <ACCESS_TOKEN>
format value | Requested file | Media type |
|---|---|---|
1 | application/pdf | |
2 | ZPL 300 DPI | application/zpl |
3 | ZPL 203 DPI | application/zpl |
4 | ZPL 600 DPI | application/zpl |
All three ZPL resolutions share the single application/zpl media type. The
requested resolution is selected by the format value, not by the media type.
Omit format only when your application intends to use the Account preference
defined by the current operation contract. See the API Reference
for complete parameter details.
Handling a binary 200
The body is binary. The API declares only application/pdf and
application/zpl for a 200 response — all three ZPL resolutions share the
one ZPL media type, so Content-Type alone does not identify which resolution
came back; the format you requested does. Branch on the actual HTTP status and Content-Type; the success body
must not be parsed as the standard JSON envelope.
- cURL
- JavaScript
- Python
- Java
curl -s -o "${DESTINATION}" -w '%{http_code} %{content_type}\n' \
"${MAILHUB_API_BASE_URL}/api/v1/labels/${LABEL_ID}/download?format=${FORMAT}" \
-H "Authorization: Bearer ${MAILHUB_ACCESS_TOKEN}"
import {writeFile} from 'node:fs/promises';
const response = await fetch(
`${apiBaseUrl}/api/v1/labels/${labelId}/download?format=${format}`,
{headers: {Authorization: `Bearer ${accessToken}`}},
);
if (response.status === 200) {
const contentType = response.headers.get('content-type')?.split(';')[0];
const supported = new Set(['application/pdf', 'application/zpl']);
if (!supported.has(contentType)) throw new Error('Unexpected Label content type');
const bytes = new Uint8Array(await response.arrayBuffer());
await writeFile(applicationChosenDestination, bytes);
}
import requests
response = requests.get(
f"{api_base_url}/api/v1/labels/{label_id}/download",
params={"format": label_format},
headers={"Authorization": f"Bearer {access_token}"},
)
if response.status_code == 200:
content_type = response.headers.get("content-type", "").split(";")[0]
if content_type not in ("application/pdf", "application/zpl"):
raise RuntimeError("Unexpected Label content type")
with open(application_chosen_destination, "wb") as file:
file.write(response.content)
// Java 11+ java.net.http — no MailHub package to install and no third-party client.
// ofByteArray keeps the body as bytes; it is never handed to a JSON reader.
var request = HttpRequest.newBuilder()
.uri(URI.create(apiBaseUrl + "/api/v1/labels/" + labelId + "/download?format=" + format))
.header("Authorization", "Bearer " + accessToken)
.GET()
.build();
HttpResponse<byte[]> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() == 200) {
String contentType = response.headers().firstValue("content-type").orElse("").split(";")[0];
if (!Set.of("application/pdf", "application/zpl").contains(contentType)) {
throw new IllegalStateException("Unexpected Label content type");
}
Files.write(applicationChosenDestination, response.body());
}
No public Content-Disposition response header or filename behavior is
declared. Select the local destination and filename in your application; do
not depend on undeclared caching, storage duration, or repeat-download behavior.
Handling 202
A 202 response means the requested Label file is pending or still being
generated.
No public response body or Retry-After header is defined. No polling or retry
interval is defined, and this guide does not promise eventual success.
Preserve the Label identifier, record the response status, avoid a tight or blind retry loop, and apply an application-owned operational policy without presenting it as a Mailhub contract.
Handling documented errors
Inspect the actual response and Label Errors for the documented boundary:
| Status | Current public description |
|---|---|
404 | Label not found or requested format not generated. |
409 | Label generation conflicts with current state. |
500 | Label generation failed or internal server error. |
These remain distinct documented outcomes. The API Reference is the complete source for every response detail.
File-handling guidance
- Treat the body as untrusted binary input.
- Validate HTTP status and
Content-Typebefore consuming bytes. - Keep binary and JSON handling paths separate.
- Select the local filename yourself and avoid logging binary content or credentials.
- Protect stored shipping Labels according to your application's data policy.
Production guidance
- Retain
postageLabel.idfrom the successful purchase response. - Make status-aware response handling explicit and use streaming where your client supports it.
- Avoid loading unnecessarily large binary responses into logs.
- Do not depend on undeclared response headers.
- Keep format handling forward-compatible while accepting only media types your application supports.
- Consult the API Reference for complete operation details.
Next steps
- Download a Label File for the copyable request and binary write.
- Review the Labels concept.
- Buy a Label when beginning the state-changing purchase workflow.
- Cancel a Label through its documented operation when applicable.
- Review Label Errors and the API Reference.