Developers

A free API. No account to enrol or to resolve.

UTF-8 JSON over HTTPS. Identity is controlled by one key per unit, not by an account. Every call is logged in the operator’s API log, with keys masked.

Base https://parcid.com/api/v1

Dark screen with blurred lines of code and a square code made of golden light.

Try it

Create your first PID.

Enrolment of class api, level 0 (registered, unverified). Pick the unit profile; dimensions and weight are optional. If you pick photos, your browser computes their SHA-256 fingerprint and only the fingerprints are sent: the files never leave your device.

Specification v0.1 · draft: routes and semantics may change before launch.

Dimensions (mm, optional)
Weight (g, optional)
Evidence (optional)

The fingerprint is computed in your browser. No file is uploaded.

    API v1

    Endpoints

    https://parcid.com/api/v1 · JSON UTF-8 · no account to enrol and resolve

    Method and pathAuthWhat it does
    Enrol and resolve
    GET /verify/{PID}No authenticationValidates format and check, normalises, and says whether it exists and its status. For label readers, forms and agents: cheap, and it does not count as a resolution.
    GET /parcels/{PID}No authenticationPublic resolution: what anyone can see (SPEC §3.4), including the unit profile and the enrolment’s anchoring.
    POST /parcelsNo account (optional developer key)Enrolment: creates the PID and returns the owner key only once, with the sealed record. JSON if you only send fingerprints; multipart if you upload files. An SSCC comes in as a link.
    GET /00/{SSCC} · outside /api/v1No authenticationGS1 Digital Link: a publicly linked SSCC resolves to its PIDs. Each carries a claim: “operator” (linked by an identified operator) or “self_declared” (unverified: anyone can link a public SSCC). The PID is not a GS1 key.
    With the owner key
    GET /parcels/{PID}Owner keyFull resolution for the owner: enrolment record, physical signature, evidence with short-lived signed URLs, private links, detailed sightings.
    POST /parcels/{PID}/linksOwner keyAdds external references (tracking, SSCC, order, BIC container, seal, GRAI, bag tag) with private or public visibility.
    POST /parcels/{PID}/lineageParents’ keyDeclares opening, consolidation, split or repack and creates the child.
    POST /parcels/{PID}/statusOwner keyChanges the status to retired (delivered and closed) or lost.
    POST /parcels/{PID}/rotate-keyOwner keyIssues a new key and revokes the previous one.
    POST /parcels/{PID}/sightingsOperator key + owner keySighting with 1:1 comparison (SPEC §8). Made by an operator: its pidd_ key with the contracted service goes in Authorization and the unit’s owner key in X-Parcid-Owner-Key.
    POST /reservationsCertified device or developer keyReserves blocks of PIDs for offline stations, signed by the device. They expire after 90 days and are never reissued.
    Proofs and anchors
    GET /parcels/{PID}/proofNo authenticationInclusion proof of every record of the unit: RFC 6962 Merkle path, signed daily statement and OpenTimestamps proof.
    GET /anchorsNo authenticationChain of daily statements: Merkle root, Ed25519 signature, chaining and Bitcoin status.
    GET /anchors/{día}No authenticationOne day’s statement and its OpenTimestamps proof; with .ots, the binary file for ots verify.
    Open data and status
    GET /exportNo authenticationIndex of daily exports: day, URL, size, fingerprint, units and signed manifest. CC0.
    GET /export/parcid-export-{AAAAMMDD}.jsonl.gzNo authenticationThe day’s full export as JSON Lines + gzip: units (by a per-unit salted tag, pid_tag + pid_salt; never the PID in clear), ledger records, anchors and a signed manifest.
    GET /.well-known/parcid · outside /api/v1No authenticationRegistry metadata: version, public keys (kid), ID format, transparency, export and limits.
    GET /statsNo authenticationAggregate figures: units by profile, level and status, records by type, latest anchor and export, 24 h requests.
    GET /healthNo authenticationService health: ok, degraded or down (503), with checks and daily jobs.
    POST /beaconNo authenticationThis site’s visit counter: path, referrer and language. No cookies or identifiers; the IP is not stored. Returns 204.

    GET /verify/{PID}

    No authentication

    Validates format and check, normalises, and says whether it exists and its status. For label readers, forms and agents: cheap, and it does not count as a resolution.

    curl https://parcid.com/api/v1/verify/pid-1-7qc3-z79d-g6rz-8e

    GET /parcels/{PID}

    No authentication

    Public resolution: what anyone can see (SPEC §3.4), including the unit profile and the enrolment’s anchoring.

    curl -H 'Accept: application/json' https://parcid.com/api/v1/parcels/PID-1-7QC3-Z79D-G6RZ-8E

    POST /parcels

    No account (optional developer key)

    Enrolment: creates the PID and returns the owner key only once, with the sealed record. JSON if you only send fingerprints; multipart if you upload files. An SSCC comes in as a link.

    curl -X POST https://parcid.com/api/v1/parcels \
      -H 'Content-Type: application/json' \
      -d '{
        "capture_class": "api",
        "unit_profile": "parcel",
        "captured_at": "2026-11-18T09:40:58Z",
        "dimensions_mm": { "l": 412, "w": 306, "h": 218, "tolerance_mm": 3 },
        "weight_g": { "value": 3840, "tolerance_g": 5, "scale_class": "consumer" },
        "evidence": [ { "role": "top", "sha256": "sha256:0c7e…", "mime": "image/jpeg", "bytes": 2418833 } ],
        "links": [ { "kind": "sscc", "value": "003412345678901238", "visibility": "public" } ],
        "consent": { "no_personal_data": true, "terms_version": "2026-10" }
      }'

    GET /00/{SSCC}

    No authenticationoutside /api/v1

    GS1 Digital Link: a publicly linked SSCC resolves to its PIDs. Each carries a claim: “operator” (linked by an identified operator) or “self_declared” (unverified: anyone can link a public SSCC). The PID is not a GS1 key.

    curl -H 'Accept: application/json' https://parcid.com/00/003412345678901238

    GET /parcels/{PID}

    Owner key

    Full resolution for the owner: enrolment record, physical signature, evidence with short-lived signed URLs, private links, detailed sightings.

    curl -H "Authorization: Bearer $PIDK" https://parcid.com/api/v1/parcels/PID-1-7QC3-Z79D-G6RZ-8E

    POST /parcels/{PID}/lineage

    Parents’ key

    Declares opening, consolidation, split or repack and creates the child.

    curl -X POST https://parcid.com/api/v1/parcels/PID-1-7QC3-Z79D-G6RZ-8E/lineage \
      -H "Authorization: Bearer $PIDK" -H 'Content-Type: application/json' \
      -d '{ "operation": "repack", "consent": { "no_personal_data": true, "terms_version": "2026-10" } }'
    # consolidate: "parents": [ { "pid": "PID-…", "owner_key": "pidk_…" } ]

    POST /parcels/{PID}/status

    Owner key

    Changes the status to retired (delivered and closed) or lost.

    curl -X POST https://parcid.com/api/v1/parcels/PID-1-7QC3-Z79D-G6RZ-8E/status \
      -H "Authorization: Bearer $PIDK" -H 'Content-Type: application/json' \
      -d '{ "status": "retired", "reason_code": "delivered" }'

    POST /parcels/{PID}/rotate-key

    Owner key

    Issues a new key and revokes the previous one.

    curl -X POST -H "Authorization: Bearer $PIDK" https://parcid.com/api/v1/parcels/PID-1-7QC3-Z79D-G6RZ-8E/rotate-key

    POST /parcels/{PID}/sightings

    Operator key + owner key

    Sighting with 1:1 comparison (SPEC §8). Made by an operator: its pidd_ key with the contracted service goes in Authorization and the unit’s owner key in X-Parcid-Owner-Key.

    curl -X POST https://parcid.com/api/v1/parcels/PID-1-7QC3-Z79D-G6RZ-8E/sightings \
      -H "Authorization: Bearer $PIDD" -H "X-Parcid-Owner-Key: $PIDK" -F capture_class=station \
      -F 'evidence[top]=@top.jpg' -F 'evidence[perspective]=@perspective.jpg'
    # $PIDD: operator key with the contracted service · without it: 402 {"error":"paid_service"}

    POST /reservations

    Certified device or developer key

    Reserves blocks of PIDs for offline stations, signed by the device. They expire after 90 days and are never reissued.

    curl -X POST https://parcid.com/api/v1/reservations -H 'Content-Type: application/json' -d '{
      "device": { "public_key": "ed25519:…" },
      "count": 500, "requested_at": "2026-11-18T09:40:00Z", "nonce": "r4nd0m-n0nce",
      "device_signature": "…"
    }'
    # device_signature: Ed25519 over sha256(JCS({schema:"parcid.reservation-request/1", public_key, count, requested_at, nonce}))

    GET /parcels/{PID}/proof

    No authentication

    Inclusion proof of every record of the unit: RFC 6962 Merkle path, signed daily statement and OpenTimestamps proof.

    curl https://parcid.com/api/v1/parcels/PID-1-7QC3-Z79D-G6RZ-8E/proof
    # verify it without us: node cli.mjs proof PID-1-7QC3-Z79D-G6RZ-8E

    GET /anchors

    No authentication

    Chain of daily statements: Merkle root, Ed25519 signature, chaining and Bitcoin status.

    curl "https://parcid.com/api/v1/anchors?from=2026-11-01&limit=30"

    GET /anchors/{día}

    No authentication

    One day’s statement and its OpenTimestamps proof; with .ots, the binary file for ots verify.

    curl https://parcid.com/api/v1/anchors/2026-11-18
    curl -o 2026-11-18.ots https://parcid.com/api/v1/anchors/2026-11-18.ots
    # ots verify 2026-11-18.ots, or: node cli.mjs ots 2026-11-18.ots --bitcoin

    GET /export

    No authentication

    Index of daily exports: day, URL, size, fingerprint, units and signed manifest. CC0.

    curl https://parcid.com/api/v1/export
    # { license: "CC0-1.0", exports: [ { day, url, bytes, sha256, units, manifest, signature } ] }

    GET /export/parcid-export-{AAAAMMDD}.jsonl.gz

    No authentication

    The day’s full export as JSON Lines + gzip: units (by a per-unit salted tag, pid_tag + pid_salt; never the PID in clear), ledger records, anchors and a signed manifest.

    curl -O https://parcid.com/api/v1/export/parcid-export-20261118.jsonl.gz
    node cli.mjs export parcid-export-20261118.jsonl.gz --pid PID-1-7QC3-Z79D-G6RZ-8E

    GET /.well-known/parcid

    No authenticationoutside /api/v1

    Registry metadata: version, public keys (kid), ID format, transparency, export and limits.

    curl https://parcid.com/.well-known/parcid

    GET /stats

    No authentication

    Aggregate figures: units by profile, level and status, records by type, latest anchor and export, 24 h requests.

    curl https://parcid.com/api/v1/stats

    GET /health

    No authentication

    Service health: ok, degraded or down (503), with checks and daily jobs.

    curl https://parcid.com/api/v1/health

    POST /beacon

    No authentication

    This site’s visit counter: path, referrer and language. No cookies or identifiers; the IP is not stored. Returns 204.

    curl -X POST https://parcid.com/api/v1/beacon -H 'Content-Type: application/json' \
      -d '{ "p": "/como-funciona/", "r": "", "l": "es" }'
    # 204 · no cookies, no identifiers, IP never stored

    The owner key

    Full control over one unit. Nothing more.

    • Format pidk_ + 52 Crockford symbols (260 bits). Returned only once, in the enrolment response.
    • The registry stores only SHA-256(key). Whoever loses it cannot recover it: there is no personal data to tie a recovery to.
    • It lets you read the whole unit, link references, declare lineage, change status and rotate the key.
    • It is sent in the Authorization: Bearer pidk_… header, never in the URL.
    • In lineage, the child is born with its own key, returned to whoever declared the operation.
    • Store it in your system next to the PID and the enrolment record, which you can verify any time at /en/verify.

    Errors

    Predictable responses.

    {"error":"code","message":"text","request_id":"…"}.

    HTTPMeaning
    200Exists (any status, including revoked)
    301Non-canonical form → canonical
    404Valid format, not registered
    422Invalid format or check
    429Rate limit
    errorWhen
    invalid_idinvalid format or check
    not_foundnot registered
    unauthorizedwrong key
    forbiddenthe status does not allow it
    payload_too_largefile too large
    unsupported_mediatype not accepted
    rate_limitedlimit reached
    consent_requiredconsent missing
    lineage_conflictinconsistent lineage operation
    paid_serviceoperator service not contracted

    Free tier

    Limits Proposal v0.1 · pending decision

    ItemProposal
    Enrolments without a developer key100 per month per IP address and per device key
    Enrolments with a free developer key (verified email)1,000 per month
    Public resolution60 per minute per IP; 600 per minute with a key
    GET /verify600 per minute per IP
    Raw evidence on the free tier30 days encrypted; then fingerprints only
    Physical signature and recordKept indefinitely
    Size per enrolment25 MB per image, 200 MB video, 50 MB point cloud; 300 MB in total
    • Enrolments without a device signature stay at level 0 and count against stricter limits.
    • Each source (device key, developer key, IP) builds a reputation: many orphan or inconsistent enrolments lower the maximum level it can obtain. Published in aggregate, not per source.