Entwickler
Logbuch-API
Lesen und schreiben Sie Ihr PingDX-Logbuch aus einem anderen Logprogramm, einem Skript oder Ihrer Website.
Auf dieser Seite
Einführung
Mit der PingDX-API kann ein Mitglied sein PingDX-Logbuch mit einem anderen Logprogramm, einem Skript oder der eigenen Website synchron halten: QSOs herunterladen, neue hinzufügen, korrigieren oder löschen. Sie gewährt nur Zugriff auf das Logbuch des Mitglieds, das den Schlüssel erstellt hat.
Basis-URL
https://pingdx.org/api/v1Anfragen und Antworten sind JSON (UTF-8). Senden Sie Content-Type: application/json bei jeder Anfrage mit Body. Datumsangaben sind ISO-8601-Zeichenketten; der Server antwortet immer in UTC (2026-10-05T18:42:00.000Z).
Eine maschinenlesbare Beschreibung (OpenAPI 3.1, öffentlich, ohne Schlüssel) steht für Codegeneratoren und Werkzeuge wie Postman zur Verfügung: https://pingdx.org/api/v1/openapi.json
Schlüssel erhalten
Jedes Mitglied erstellt seine eigenen Schlüssel unter Profil → API-Zugang. Geben Sie jedem Schlüssel einen Namen (das Programm oder die Website, die ihn verwendet) und wählen Sie seine Rechte:
- Nur Lesen (
READ): kann das Logbuch lesen (GET-Anfragen) — ausreichend, um Ihre QSOs auf einer Website anzuzeigen. - Lesen und Schreiben (
WRITE): kann zusätzlich QSOs erstellen, ändern und löschen — nötig für eine Zwei-Wege-Synchronisation mit einem Logprogramm. - Der vollständige Schlüssel wird nur einmal angezeigt, direkt nach der Erstellung: Kopieren Sie ihn sofort. PingDX speichert nur einen Fingerabdruck davon und kann ihn nicht erneut anzeigen.
- Sie können einen Schlüssel jederzeit widerrufen: Er funktioniert dann sofort nicht mehr. Maximal 10 aktive Schlüssel pro Mitglied.
Sicherheit
- Behandeln Sie einen Schlüssel wie ein Passwort. Legen Sie niemals einen WRITE-Schlüssel im öffentlichen Code einer Website ab (JavaScript, das an die Browser der Besucher gesendet wird): Jeder könnte ihn lesen und Ihr Logbuch ändern. Rufen Sie die API von Ihrem Server aus auf oder verwenden Sie einen READ-Schlüssel.
- Ein Schlüssel öffnet nur die Logbuch-API: niemals Ihre Nachrichten, Ihr Profil, Ihr Passwort oder den Rest Ihres Kontos.
- Verwenden Sie einen Schlüssel pro Programm, damit Sie einen widerrufen können, ohne die anderen zu beeinträchtigen. Schlüssel beginnen mit
pdx_…: Das ist leicht zu erkennen, falls einer versehentlich veröffentlicht wird — widerrufen Sie ihn dann.
Authentifizierung
Senden Sie den Schlüssel bei jeder Anfrage im Header Authorization (Bearer) oder im Header X-API-Key. Ihre PingDX-Anmeldesitzung wird von dieser API nicht akzeptiert, und Cookies werden nie verwendet.
Authorization: Bearer pdx_…
# oder
X-API-Key: pdx_…Ohne gültigen Schlüssel antwortet die API mit 401; ein READ-Schlüssel, der schreiben will, erhält 403:
401 API_KEYS.INVALIDFehlender, unbekannter oder widerrufener Schlüssel (oder Konto nicht mehr aktiv).403 API_KEYS.READ_ONLYDer Schlüssel ist schreibgeschützt: Erstellen Sie einen WRITE-Schlüssel, um zu schreiben.
Endpunkte
Alle Pfade sind relativ zur Basis-URL. Jedes QSO wird durch seine clientId identifiziert, eine von Ihrem Programm gewählte UUID.
GET/me
Erforderliche Rechte: READ- oder WRITE-Schlüssel
Das Mitglied, zu dem der Schlüssel gehört (Rufzeichen, Locator), und die Rechte des Schlüssels. Praktisch, um einen Schlüssel zu prüfen.
Antwort
{
"callsign": "14KM001",
"locator": "JN03ql",
"scope": "WRITE"
}Fehler
401 API_KEYS.INVALIDFehlender, unbekannter oder widerrufener Schlüssel (oder Konto nicht mehr aktiv).429 GENERIC.RATE_LIMITEDZu viele Anfragen: Warten Sie kurz und versuchen Sie es erneut.
GET/logs
Erforderliche Rechte: READ- oder WRITE-Schlüssel
Das Logbuch, älteste Änderung zuerst (sortiert nach dem letzten serverseitigen Schreibvorgang), seitenweise.
Ohne updatedSince erhalten Sie das gesamte Logbuch. Mit diesem Parameter nur die QSOs, die nach diesem Zeitpunkt erstellt, geändert oder kreuzbestätigt wurden.
Parameter
updatedSincestring (ISO 8601)QueryoptionalISO-8601-Datum mit Uhrzeit und Zeitzone (z. B.
2026-10-01T00:00:00Z). Übergeben Sie dieserverTimeIhrer vorherigen Synchronisation.limitinteger 1–500 (200)QueryoptionalQSOs pro Seite, von 1 bis 500. Standard: 200.
cursorstringQueryoptionalDer
nextCursorder vorherigen Seite, um die nächste zu erhalten. Behalten Sie beim Blättern dasselbeupdatedSincebei.
Antwort
items: die QSOs der Seite (siehe QSO-Objekt). nextCursor: als cursor übergeben, um die nächste Seite zu erhalten; null auf der letzten Seite. serverTime: die Serverzeit zu Beginn der Anfrage — Ihr nächstes updatedSince.
{
"items": [
{
"id": "cmgd4w1x70003s60e8k2v9qhz",
"clientId": "0b9e6c1e-5f3a-4d2b-9c7e-1a2b3c4d5e6f",
"band": "ELEVEN_METERS",
"frequencyMhz": "27.555",
"channel": null,
"ctcssTone": null,
"dcsCode": null,
"mode": "USB",
"callsignWorked": "14KM123",
"operatorCallsign": "14KM001",
"dxLocator": "JN18du",
"dxLat": null,
"dxLng": null,
"txPowerW": 4,
"myLocator": "JN03ql",
"splitKhz": null,
"path": null,
"qsoStatus": null,
"rstSent": "59",
"rstReceived": "57",
"qslSent": false,
"qslReceived": false,
"qslVia": null,
"qsoAt": "2026-10-05T18:42:00.000Z",
"notes": null,
"radio": "President Lincoln II+",
"antenna": "Sirio 827",
"crossConfirmedAt": null,
"spottedAt": null,
"createdAt": "2026-10-05T18:43:10.512Z",
"syncedAt": "2026-10-05T18:43:10.512Z"
}
],
"nextCursor": null,
"serverTime": "2026-10-05T18:45:12.345Z"
}Fehler
400 VALIDATION.FAILEDUngültige Anfrage: ein fehlerhafter Parameter, eine clientId, die keine UUID ist, oder ein Batch ohne gültiges Array entries (1 bis 200). details beschreibt das Problem.401 API_KEYS.INVALIDFehlender, unbekannter oder widerrufener Schlüssel (oder Konto nicht mehr aktiv).429 GENERIC.RATE_LIMITEDZu viele Anfragen: Warten Sie kurz und versuchen Sie es erneut.
GET/logs/ids
Erforderliche Rechte: READ- oder WRITE-Schlüssel
Die IDs aller QSOs im Logbuch, ohne Seitenaufteilung. Vergleichen Sie sie mit Ihrer Kopie, um die auf PingDX (oder aus einem anderen Programm) gelöschten QSOs zu finden.
Antwort
clientId kann bei einigen sehr alten QSOs, die vor Einführung der Synchronisation erstellt wurden, null sein.
{
"items": [
{ "id": "cmgd4w1x70003s60e8k2v9qhz", "clientId": "0b9e6c1e-5f3a-4d2b-9c7e-1a2b3c4d5e6f" },
{ "id": "cmgd51b2c0007s60efq3m1abc", "clientId": "7d1f2a90-3b4c-4e5f-8a6b-9c0d1e2f3a4b" }
]
}Fehler
401 API_KEYS.INVALIDFehlender, unbekannter oder widerrufener Schlüssel (oder Konto nicht mehr aktiv).429 GENERIC.RATE_LIMITEDZu viele Anfragen: Warten Sie kurz und versuchen Sie es erneut.
POST/logs/batch
Erforderliche Rechte: WRITE-Schlüssel
Erstellt oder aktualisiert bis zu 200 QSOs in einer Anfrage. Ein QSO mit unbekannter clientId wird erstellt; eine bestehende clientId aktualisiert dieses QSO.
Jedes QSO wird einzeln validiert: Ein ungültiges QSO wird in seinem Ergebnis gemeldet und verhindert nie das Speichern der anderen. Die Anfrage schlägt nur als Ganzes fehl (400), wenn entries fehlt, leer oder länger als 200 ist.
Parameter
entriesQSO[] (1–200)BodyerforderlichDie zu erstellenden oder zu aktualisierenden QSOs (siehe QSO-Objekt).
Request-Body
{
"entries": [
{
"clientId": "0b9e6c1e-5f3a-4d2b-9c7e-1a2b3c4d5e6f",
"band": "ELEVEN_METERS",
"frequencyMhz": "27.555",
"mode": "USB",
"callsignWorked": "14KM123",
"qsoAt": "2026-10-05T18:42:00Z",
"rstSent": "59",
"rstReceived": "57",
"dxLocator": "JN18DU"
},
{
"clientId": "7d1f2a90-3b4c-4e5f-8a6b-9c0d1e2f3a4b",
"band": "PMR446",
"channel": 8,
"ctcssTone": "67.0",
"mode": "FM",
"callsignWorked": "PMR-ALPHA",
"qsoAt": "2026-10-05T19:05:00+02:00",
"dxLocator": "JN1"
}
]
}Antwort
Ein Ergebnis pro QSO. id ist die Server-ID oder null, wenn das QSO abgelehnt wurde; error nennt dann den Grund. permanent: true bedeutet, dass das QSO selbst ungültig ist: Es wird erneut abgelehnt, bis es korrigiert ist — nicht unverändert wiederholen. Eine Ablehnung ohne permanent ist vorübergehend: Versuchen Sie es später erneut.
{
"results": [
{ "clientId": "0b9e6c1e-5f3a-4d2b-9c7e-1a2b3c4d5e6f", "id": "cmgd4w1x70003s60e8k2v9qhz", "spottedAt": null },
{ "clientId": "7d1f2a90-3b4c-4e5f-8a6b-9c0d1e2f3a4b", "id": null, "error": "LOCATOR_INVALID", "permanent": true }
]
}Fehler
400 VALIDATION.FAILEDUngültige Anfrage: ein fehlerhafter Parameter, eine clientId, die keine UUID ist, oder ein Batch ohne gültiges Array entries (1 bis 200). details beschreibt das Problem.401 API_KEYS.INVALIDFehlender, unbekannter oder widerrufener Schlüssel (oder Konto nicht mehr aktiv).403 API_KEYS.READ_ONLYDer Schlüssel ist schreibgeschützt: Erstellen Sie einen WRITE-Schlüssel, um zu schreiben.429 GENERIC.RATE_LIMITEDZu viele Anfragen: Warten Sie kurz und versuchen Sie es erneut.
DELETE/logs/{clientId}
Erforderliche Rechte: WRITE-Schlüssel
Löscht ein QSO Ihres Logbuchs anhand seiner clientId. Wurde es an den DX-Cluster gesendet, wird auch der Spot entfernt.
Parameter
clientIdstring (UUID)PfaderforderlichDie
clientIddes QSO (eine UUID).
Antwort
Kein Body (204). Senden Sie diese Anfrage ohne Body und ohne Content-Type.
HTTP/1.1 204 No ContentFehler
400 VALIDATION.FAILEDUngültige Anfrage: ein fehlerhafter Parameter, eine clientId, die keine UUID ist, oder ein Batch ohne gültiges Array entries (1 bis 200). details beschreibt das Problem.401 API_KEYS.INVALIDFehlender, unbekannter oder widerrufener Schlüssel (oder Konto nicht mehr aktiv).403 API_KEYS.READ_ONLYDer Schlüssel ist schreibgeschützt: Erstellen Sie einen WRITE-Schlüssel, um zu schreiben.404 GENERIC.NOT_FOUNDKein QSO mit dieser clientId in Ihrem Logbuch.429 GENERIC.RATE_LIMITEDZu viele Anfragen: Warten Sie kurz und versuchen Sie es erneut.
GET/openapi.json
Erforderliche Rechte: keine (öffentlich)
Die OpenAPI-3.1-Beschreibung dieser API (JSON). Öffentlich: kein Schlüssel nötig.
Das QSO-Objekt
Dasselbe Objekt wird an POST /logs/batch gesendet und von GET /logs zurückgegeben. Das Feld band wählt eine von zwei Varianten:
- 11 m (CB) —
"band": "ELEVEN_METERS":frequencyMhz(optional);channel,ctcssToneunddcsCodemüssen fehlen odernullsein. - PMR446 —
"band": "PMR446":channel(1 bis 16) ist erforderlich,ctcssTone/dcsCodeoptional;frequencyMhzmuss fehlen odernullsein. - Eine Aktualisierung ersetzt das QSO: Senden Sie immer das vollständige QSO. Ein ausgelassenes Feld wird zurückgesetzt (leer bzw.
falsebeiqslSent/qslReceived), außeroperatorCallsign,dxLat,dxLng,txPowerW,radioundantenna, die bei Auslassung ihren gespeicherten Wert behalten. - Text wird von Leerzeichen am Rand befreit, Locator werden normalisiert (
jn18DU→JN18du) und unbekannte Felder werden ignoriert. Schreibgeschützte Felder werden vom Server gesetzt: Sie müssen nicht gesendet werden.
clientIderforderlichstring (UUID)Ihre stabile ID für dieses QSO: eine zufällige UUID (z. B.
crypto.randomUUID()), einmalig erzeugt und mit dem QSO in Ihrem Programm gespeichert. Sie ist der Synchronisationsschlüssel.banderforderlich"ELEVEN_METERS" | "PMR446"Das Band:
ELEVEN_METERS(11 m / CB) oderPMR446.qsoAterforderlichstring (ISO 8601)Datum und Uhrzeit des QSO, ISO 8601 mit Zeitzone (
Zoder+02:00). In UTC gespeichert und zurückgegeben.frequencyMhzoptionalstring "27.555" · 26.000–28.000Nur 11 m: die Frequenz in MHz als Zeichenkette mit genau 3 Nachkommastellen, zwischen 26.000 und 28.000.
channelbei PMR446 erforderlichinteger 1–16Nur PMR446 (dort erforderlich): der Kanal, 1 bis 16.
ctcssToneoptionalstring "67.0" · ^\d{2,3}\.\d$Nur PMR446: CTCSS-Ton in Hz, eine Nachkommastelle (
67.0,103.5).dcsCodeoptionalstring "D023N" · ^D?[0-7]{3}[NI]?$Nur PMR446: DCS-Code, 3 Oktalziffern mit optionalem Präfix
Dund PolaritätN/I(D023N).modeoptional"FM" | "AM" | "SSB" | "USB" | "LSB" | "CW" | "DIGITAL"Der Modus.
callsignWorkedoptionalstring ≤ 20Das Rufzeichen der gearbeiteten Station.
operatorCallsignoptionalstring ≤ 20Ihr eigenes Rufzeichen für dieses QSO (nützlich, wenn Sie mehrere verwenden). Bleibt bei einer Aktualisierung ohne Angabe erhalten.
dxLocatoroptionalstring "JN18" | "JN18du"Maidenhead-Locator der gearbeiteten Station, 4 oder 6 Zeichen.
dxLatoptionalnumber −90…90Exakter Breitengrad der gearbeiteten Station, falls bekannt. Karten bevorzugen ihn gegenüber dem Locator.
dxLngoptionalnumber −180…180Exakter Längengrad der gearbeiteten Station, falls bekannt.
txPowerWoptionalinteger 0–100000Ihre Sendeleistung in ganzen Watt.
myLocatoroptionalstring "JN03" | "JN03ql"Ihr Locator für dieses QSO (z. B. portabel), 4 oder 6 Zeichen.
splitKhzoptionalinteger −99999…99999Split in kHz (Differenz zwischen Sende- und Empfangsfrequenz).
pathoptional"SP" | "LP"Ausbreitungsweg: kurzer Weg (
SP) oder langer Weg (LP).qsoStatusoptional"HRD" | "WKD" | "CFM"Kontaktstatus: gehört (
HRD), gearbeitet (WKD), bestätigt (CFM).rstSentoptionalstring ≤ 10Gesendeter Rapport (z. B.
59).rstReceivedoptionalstring ≤ 10Erhaltener Rapport.
qslSentoptionalboolean (false)QSL-Karte gesendet. Standard:
false.qslReceivedoptionalboolean (false)QSL-Karte erhalten. Standard:
false.qslViaoptional"DIRECT" | "BUREAU" | "EQSL" | "ECARD"Auf welchem Weg die QSL ging:
DIRECT(Post),BUREAU,EQSL(eQSL.cc) oderECARD(andere elektronische Karte).notesoptionalstring ≤ 2000Freie Notizen.
radiooptionalstring ≤ 80Das verwendete Funkgerät (Freitext).
antennaoptionalstring ≤ 80Die verwendete Antenne (Freitext).
sendSpotoptionalbooleanNur Schreiben, wird nie zurückgegeben:
trueveröffentlicht das QSO zusätzlich als Spot im PingDX-DX-Cluster (einmal pro QSO). Nur für Live-QSOs, nie für Importe alter QSOs.idschreibgeschütztstringDie Server-ID des QSO.
crossConfirmedAtschreibgeschütztstring (ISO 8601) | nullWird von PingDX gesetzt, wenn die Gegenstation dasselbe QSO auf PingDX geloggt hat (Kreuzbestätigung).
spottedAtschreibgeschütztstring (ISO 8601) | nullZeitpunkt, an dem das QSO als Cluster-Spot veröffentlicht wurde, oder
null.createdAtschreibgeschütztstring (ISO 8601)Zeitpunkt, an dem das QSO erstmals auf PingDX gespeichert wurde.
syncedAtschreibgeschütztstring (ISO 8601)Letzter serverseitiger Schreibvorgang des QSO (bestimmt die Reihenfolge von
GET /logs).
Synchronisationsleitfaden
Eine robuste Zwei-Wege-Synchronisation zwischen Ihrem Programm und PingDX in sechs Schritten:
- 1
Erster vollständiger Download
Rufen Sie
GET /logs?limit=500ohneupdatedSinceauf und folgen SienextCursor, bis ernullist. Merken Sie sich dieserverTimeder ersten Seite. - 2
Inkrementeller Download
Rufen Sie beim nächsten Mal
GET /logs?updatedSince=…mit der gespeichertenserverTimeauf, folgen Sie den Seiten auf dieselbe Weise und speichern Sie die neueserverTime(weiterhin von der ersten Seite) erst, wenn alle Seiten verarbeitet wurden. Ein QSO doppelt zu erhalten ist harmlos: Wenden Sie es anhand derclientIdan. - 3
QSOs senden
Weisen Sie jedem QSO Ihres Programms einmalig eine UUID als
clientIdzu und speichern Sie sie. Senden Sie neue oder geänderte QSOs mitPOST /logs/batch, höchstens 200 pro Anfrage. Dasselbe QSO erneut zu senden ist sicher: Es wird aktualisiert, statt ein Duplikat zu erzeugen. - 4
Abgelehnte QSOs
Lesen Sie jedes Ergebnis: Ist
idgleichnullmitpermanent: true, zeigen Sie das QSO dem Benutzer zur Korrektur an (errornennt den Grund), statt es zu wiederholen. Ohnepermanentversuchen Sie es später erneut. - 5
Löschungen
Zum Löschen auf PingDX:
DELETE /logs/{clientId}. Um auf PingDX gelöschte QSOs zu finden:GET /logs/ids, dann aus Ihrer Kopie jedes QSO entfernen, dessenclientIdnicht mehr aufgeführt ist. - 6
Konflikte
Der letzte Schreibvorgang gewinnt, pro
clientId: Es gibt kein feldweises Zusammenführen. Die PingDX-App übernimmt über die API geschriebene QSOs bei ihrer nächsten Synchronisation (beim Öffnen oder innerhalb weniger Sekunden, wenn sie online ist).
Fehler und Limits
Fehler verwenden den HTTP-Statuscode und einen JSON-Body mit einem stabilen Code (nie einen übersetzten Text):
{ "error": { "code": "API_KEYS.READ_ONLY" } }400 VALIDATION.FAILEDUngültige Anfrage: ein fehlerhafter Parameter, eine clientId, die keine UUID ist, oder ein Batch ohne gültiges Array entries (1 bis 200). details beschreibt das Problem.
401 API_KEYS.INVALIDFehlender, unbekannter oder widerrufener Schlüssel (oder Konto nicht mehr aktiv).
403 API_KEYS.READ_ONLYDer Schlüssel ist schreibgeschützt: Erstellen Sie einen WRITE-Schlüssel, um zu schreiben.
404 GENERIC.NOT_FOUNDKein QSO mit dieser clientId in Ihrem Logbuch.
429 GENERIC.RATE_LIMITEDZu viele Anfragen: Warten Sie kurz und versuchen Sie es erneut.
500 GENERIC.INTERNAL_ERRORServerfehler: Versuchen Sie es später erneut.
Ablehnungsgründe pro QSO
Bei POST /logs/batch hat ein abgelehntes QSO id: null und einen error-Code:
CLIENT_ID_INVALIDclientId ist keine gültige UUID.DATETIME_INVALIDqsoAt ist kein ISO-8601-Datum mit Uhrzeit und Zeitzone.LOCATOR_INVALIDdxLocator oder myLocator ist kein Maidenhead-Locator mit 4 oder 6 Zeichen.FREQUENCY_FORMAT_INVALIDfrequencyMhz muss eine Zeichenkette mit 3 Nachkommastellen sein (27.555).FREQUENCY_OUT_OF_BAND_11MfrequencyMhz liegt außerhalb von 26.000–28.000 MHz.CHANNEL_OUT_OF_RANGEPMR446-Kanal außerhalb von 1–16.CTCSS_FORMAT_INVALIDctcssTone muss wie 67.0 aussehen.DCS_FORMAT_INVALIDdcsCode muss wie D023N aussehen.CLIENT_ID_CONFLICTDiese clientId gehört bereits zum QSO eines anderen Mitglieds: Erzeugen Sie eine neue UUID.
Andere Schemaverletzungen (fehlendes Feld, unbekannter Wert in einem Enum, zu langer Text …) liefern die Meldung des Validators auf Englisch, z. B. Invalid input: expected string, received undefined, ebenfalls mit permanent: true.
Limits
- 300 Anfragen pro Minute pro IP-Adresse, darüber hinaus
429(der HeaderRetry-Aftergibt die Wartezeit an). Fassen Sie Ihre QSOs in Batches zusammen, statt sie einzeln zu senden. - Höchstens 200 QSOs pro
POST /logs/batch. - 1 bis 500 QSOs pro Seite von
GET /logs(standardmäßig 200). - Höchstens 10 aktive Schlüssel pro Mitglied.
Aufruf aus einem Browser (CORS)
/api/v1 akzeptiert Anfragen von jedem Origin (CORS), ohne Cookies oder Anmeldedaten. Eine Webseite kann sie daher direkt aufrufen — aber nur mit einem READ-Schlüssel, da der Code einer öffentlichen Seite für alle sichtbar ist.
Beispiele
Ersetzen Sie pdx_… durch Ihren Schlüssel. Diese Beispiele sind kopierfertig; nur die Kommentare sind auf Englisch.
curl
# Who am I? (checks the key)
curl -H "Authorization: Bearer pdx_…" https://pingdx.org/api/v1/me
# Everything changed since my last sync
curl -H "Authorization: Bearer pdx_…" \
"https://pingdx.org/api/v1/logs?updatedSince=2026-10-01T00:00:00Z&limit=500"
# Create or update a QSO (WRITE key)
curl -X POST -H "Authorization: Bearer pdx_…" \
-H "Content-Type: application/json" \
https://pingdx.org/api/v1/logs/batch \
-d '{"entries":[{"clientId":"0b9e6c1e-5f3a-4d2b-9c7e-1a2b3c4d5e6f",
"band":"ELEVEN_METERS","frequencyMhz":"27.555","mode":"USB",
"callsignWorked":"14KM123","qsoAt":"2026-10-05T18:42:00Z",
"rstSent":"59","rstReceived":"57"}]}'
# Delete it (no body, no Content-Type)
curl -X DELETE -H "Authorization: Bearer pdx_…" \
https://pingdx.org/api/v1/logs/0b9e6c1e-5f3a-4d2b-9c7e-1a2b3c4d5e6fJavaScript (fetch)
Funktioniert mit Node.js 18+ und Deno. Behalten Sie den Schlüssel serverseitig (Umgebungsvariable).
const BASE = "https://pingdx.org/api/v1";
const KEY = process.env.PINGDX_API_KEY; // pdx_… — keep it server-side
async function call(method, path, body) {
const res = await fetch(BASE + path, {
method,
headers: {
Authorization: `Bearer ${KEY}`,
...(body ? { "Content-Type": "application/json" } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
if (res.status === 204) return null;
const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.error?.code}`);
return data;
}
// Full download, page by page
async function pullAll(updatedSince) {
const qsos = [];
let cursor = null;
let since = null;
do {
const q = new URLSearchParams({ limit: "500" });
if (updatedSince) q.set("updatedSince", updatedSince);
if (cursor) q.set("cursor", cursor);
const page = await call("GET", `/logs?${q}`);
since ??= page.serverTime; // keep the FIRST page's serverTime
qsos.push(...page.items);
cursor = page.nextCursor;
} while (cursor);
return { qsos, nextUpdatedSince: since };
}
// Push local QSOs (create or update by clientId)
const { results } = await call("POST", "/logs/batch", {
entries: [{
clientId: crypto.randomUUID(), // store it with your QSO, reuse it forever
band: "PMR446", channel: 8, ctcssTone: "67.0", mode: "FM",
callsignWorked: "PMR-ALPHA", qsoAt: new Date().toISOString(),
}],
});
for (const r of results) if (r.id === null) console.warn(r.clientId, r.error, r.permanent);Python (requests)
import os, uuid, requests
BASE = "https://pingdx.org/api/v1"
S = requests.Session()
S.headers["Authorization"] = f"Bearer {os.environ['PINGDX_API_KEY']}" # pdx_…
def pull(updated_since=None):
"""Every QSO changed since updated_since (all of them if None)."""
qsos, cursor, next_since = [], None, None
while True:
params = {"limit": 500}
if updated_since:
params["updatedSince"] = updated_since
if cursor:
params["cursor"] = cursor
r = S.get(f"{BASE}/logs", params=params, timeout=30)
r.raise_for_status()
page = r.json()
next_since = next_since or page["serverTime"] # first page's serverTime
qsos += page["items"]
cursor = page["nextCursor"]
if not cursor:
return qsos, next_since
def push(entries):
"""Create or update up to 200 QSOs; returns the rejected ones."""
r = S.post(f"{BASE}/logs/batch", json={"entries": entries}, timeout=30)
r.raise_for_status()
return [x for x in r.json()["results"] if x["id"] is None]
rejected = push([{
"clientId": str(uuid.uuid4()), # keep it with your QSO
"band": "ELEVEN_METERS", "frequencyMhz": "27.555", "mode": "USB",
"callsignWorked": "14KM123", "qsoAt": "2026-10-05T18:42:00Z",
}])
# Deleted on PingDX? Compare clientIds with your copy
server_ids = {x["clientId"] for x in S.get(f"{BASE}/logs/ids", timeout=30).json()["items"]}Eine Frage oder ein Programm, das Sie anbinden möchten? Schreiben Sie uns über die Kontaktseite.