A scheduled job usually only needs to know whether Datalink has generated a new report since its last run. The /datalink/report endpoint lets it check without downloading anything.
Check for a new report
- A
HEADrequest returns the response headers of the newest report, without a body. TheX-Xeneta-Report-Idheader tells you which report is the newest. - A
GETorHEADrequest with theIf-None-Match: "<report_id>"header returns304 - Not Modifiedfor as long as that report is still the newest. Once a newer report is available,GETreturns200with the new report object, andHEADreturns200with its headers.
The If-None-Match header accepts the ETag value exactly as it was returned, for example "jB47D", as well as weak (W/"jB47D") and comma-separated values.
Neither a HEAD request nor a 304 response creates a download link.
Before the first reportA configuration that has not generated a report yet returns
404with the codeno_report_yetand aRetry-After: 300header. Be sure to wait at least that many seconds before asking again.
Polling workflow
- Store the
report_idof the last report you downloaded. - On each run, send a
GETrequest with theIf-None-Match: "<report_id>"header. - On
304, there is nothing new, so you can stop. - On
200, download the file fromurlwithin 300 seconds, and then store the newreport_id. - On
404with the codeno_report_yet, try again after the number of seconds in theRetry-Afterheader.
Check with curl
curl -sS -I -H "X-Auth: $XENETA_API_KEY" -H 'If-None-Match: "jB47D"' \
"https://api.xeneta.com/v3.0/datalink/report?configuration_id=xArk"A 304 response means there is nothing new. A 200 response with a new ETag means a newer report is available, so request it with GET and download it. A 404 response with the code no_report_yet means you should try again later.
Sample polling script
The sample Python script below follows the workflow above. It keeps the report_id of the last downloaded report in a local file and downloads a report only when a newer one is available.
This script serves as an example and is not meant to be used as-is, so be sure to adjust it to your specific needs.
import os
import pathlib
import sys
import requests
API_URL = "https://api.xeneta.com/v3.0/datalink/report"
CONFIGURATION_ID = "xArk"
STATE_FILE = pathlib.Path("last_report_id.txt")
headers = {"X-Auth": os.environ["XENETA_API_KEY"]}
if STATE_FILE.exists():
headers["If-None-Match"] = f'"{STATE_FILE.read_text().strip()}"'
resp = requests.get(
API_URL, params={"configuration_id": CONFIGURATION_ID}, headers=headers, timeout=60
)
if resp.status_code == 304:
print("No new report")
sys.exit(0)
if resp.status_code == 404 and resp.json()["detail"]["code"] == "no_report_yet":
print(f"No report yet, try again in {resp.headers['Retry-After']} seconds")
sys.exit(0)
resp.raise_for_status()
report = resp.json()
file_path = pathlib.Path(f"{report['report_id']}.{report['format']}")
with requests.get(report["url"], stream=True, timeout=60) as download:
download.raise_for_status()
with file_path.open("wb") as file:
for chunk in download.iter_content(chunk_size=1024 * 1024):
file.write(chunk)
STATE_FILE.write_text(report["report_id"])
print(f"Downloaded {file_path}")