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.
A download has three outcomes, and they are not interchangeable:
200— the Label file bytes, asapplication/pdforapplication/zpl. Not the JSON envelope; consume the body as binary.202— the file is still being generated. The response declares aRetry-Afterheader giving the whole number of seconds to wait before requesting the same download again. Read the interval from that header rather than hard-coding one. This is the only response in the public contract that declaresRetry-After; the Label-purchase202does not.- a documented error — inspect the status and the flat error body. A
404means the Label was not found, or the requested format has not been generated.
Two more boundaries worth stating plainly:
- A Label can exist before its file does.
labelProcessingStatusmoves fromprocessingtocompleted; until it does, a download answers202and the Order's label URLs may be null or empty. - There are two routes to the same bytes. This operation takes your bearer
token and
labelIdin the path. The Order also carries time-limited links that download directly with noAuthorizationheader — treat those as credentials, and re-read the Order for fresh ones when they expire. See Binary Labels.
The four printable formats are 1 PDF, 2 ZPL 300 DPI, 3 ZPL 203 DPI, and
4 ZPL 600 DPI. A requested format that has not been generated is reported as
unavailable with 404; Mailhub never substitutes another format.
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.
The response carries a Retry-After header giving the number of seconds to wait
before retrying this download. Wait at least that long. No overall polling
schedule, retry budget, or deadline 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.