Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: SMKlog Quote Tracking API
version: 1.5.0
summary: Live parcel rates from a US origin, a checkout handed to a human, and where that checkout stands.
description: 'Live parcel rates from USPS, UPS, FedEx and DHL Express for a single shipment sent from a US origin: inside the US, or to Canada, the UK, Germany or Australia. Describe the item in plain words and the packed box size and weight are estimated server-side; supply exact dimensions and weight to skip the estimate. No account and no key: only a create-or-update keyed on your own external_id, or a session write that carries an Idempotency-Key, needs a bearer token, from an OAuth client any agent registers itself at /oauth/register. Rate limited per client (about 80 quote requests per hour); quotes cost real money to produce, so cache on your side and do not poll. Buying a label happens on smklog.com — this API prices and hands the payment to a human, it does not sell. It also tracks labels bought on smklog.com, reports whether a label payment was refunded, serves the monthly parcel shipping price index, works out the billable (dimensional) weight of a box, and lists the carriers’ holiday send-by dates and SMKlog’s shipping restrictions.'
termsOfService: https://smklog.com/terms
contact:
name: SMKlog
email: info@smklog.com
url: https://smklog.com/developers/
x-publisher: SMKlog (SMKLOG-2022 LLC)
servers:
- url: https://quote-api.smklog.com
description: 'Production. There is no sandbox host: quoting is free and read-only, so production is the place to try it.'
security:
- {}
- clientCredentials:
- quote
tags:
- name: Tracking
description: 'Shipment tracking and visibility for labels bought on smklog.com: the latest status, the carrier scan events and the estimated delivery date.'
paths:
/tracking/{tracking_number}:
get:
operationId: trackParcel
tags:
- Tracking
x-agentic-access:
action-class: connected
consequence: read
subject: optional
audience: https://quote-api.smklog.com
token:
max-ttl: 3600
audit: none
summary: Track a shipment whose label was bought on smklog.com
description: 'Package tracking for a shipping label bought on smklog.com, by carrier tracking number: the current status, up to eight scan events (newest first) and the estimated delivery date when the carrier gives one. SMKlog labels only, not a universal tracker: a number SMKlog never sold answers 404 tracking_not_found. The number is matched after removing spaces and hyphens, ignoring case; under 6 characters after that it answers 400 tracking_number_required. getCheckoutStatus hands back the number once a payment session reaches label_ready. The REST twin of the track_parcel MCP tool and the same bucket (25 lookups an hour per client); no quote allowance. The answer never carries names, addresses, emails or order ids.'
parameters:
- name: tracking_number
in: path
required: true
schema:
type: string
minLength: 6
maxLength: 80
description: 'The carrier tracking number of a label bought on smklog.com. Typical shapes: USPS 20 to 22 digits (13 characters ending in US for international), UPS 1Z plus 16 characters, FedEx 12 to 22 digits.'
example: '9400100000000000000000'
responses:
'200':
description: tracking (tracking_code, carrier, status, status_label, status_detail, last_event_at, estimated_delivery, delivered_at, updated_at), events and message.
headers:
RateLimit-Limit:
$ref: '#/components/headers/RateLimit-Limit'
RateLimit-Remaining:
$ref: '#/components/headers/RateLimit-Remaining'
RateLimit-Reset:
$ref: '#/components/headers/RateLimit-Reset'
RateLimit-Policy:
$ref: '#/components/headers/RateLimit-Policy'
content:
application/json:
schema:
$ref: '#/components/schemas/TrackingResult'
example:
tracking:
tracking_code: '9400100000000000000000'
carrier: USPS
status: in_transit
status_label: In transit
status_detail: Arrived at USPS Regional Facility
last_event_at: '2026-10-14T09:12:00Z'
estimated_delivery: '2026-10-16'
delivered_at: ''
updated_at: '2026-10-14T09:40:03.000Z'
events:
- status: in_transit
status_label: In transit
message: Arrived at USPS Regional Facility
datetime: '2026-10-14T09:12:00Z'
city: ATLANTA
state: GA
zip: '30320'
- status: in_transit
status_label: In transit
message: Departed USPS Facility
datetime: '2026-10-13T22:47:00Z'
city: KEARNY
state: NJ
zip: '07032'
- status: pre_transit
status_label: Label created
message: Shipping Label Created, USPS Awaiting Item
datetime: '2026-10-12T15:20:00Z'
city: NEWARK
state: NJ
zip: '07102'
message: In transit. SMKlog will update this page as carrier scan events arrive.
'400':
description: tracking_number_required — the number is under 6 characters once spaces and hyphens are removed.
content:
application/json:
schema:
$ref: '#/components/schemas/Problem'
example:
error: tracking_number_required
message: Enter a SMKlog shipment tracking number.
'404':
description: tracking_not_found — SMKlog never sold a label with this number; this is not a universal tracker.
content:
application/json:
schema:
$ref: '#/components/schemas/Problem'
example:
error: tracking_not_found
message: No SMKlog shipment was found for this tracking number. Tracking is available here for labels created through SMKlog after the carrier scan is received.
'429':
description: rate_limited — 25 lookups an hour per client, shared with the track_parcel tool. RateLimit-Reset and Retry-After say how many seconds until the hourly window turns.
content:
application/json:
schema:
$ref: '#/components/schemas/Problem'
example:
error: rate_limited
message: Too many Smart Quote API requests from this client. Try again later.
headers:
RateLimit-Limit:
$ref: '#/components/headers/RateLimit-Limit'
RateLimit-Remaining:
$ref: '#/components/headers/RateLimit-Remaining'
RateLimit-Reset:
$ref: '#/components/headers/RateLimit-Reset'
RateLimit-Policy:
$ref: '#/components/headers/RateLimit-Policy'
Retry-After:
$ref: '#/components/headers/Retry-After'
components:
headers:
Retry-After:
description: 'Seconds to wait before retrying (RFC 9110 section 10.2.3). On 429: until the hourly window turns, equal to RateLimit-Reset, or until 00:00 UTC for daily_session_limit. On 409 idempotency_key_in_progress: until the first call with the same key has its answer; absent on the other 409 answers.'
schema:
type: integer
minimum: 1
example: 1740
RateLimit-Limit:
description: Requests this client may make in the current one-hour window of the bucket the operation draws on (IETF draft-ietf-httpapi-ratelimit-headers).
schema:
type: integer
minimum: 1
example: 80
RateLimit-Remaining:
description: Requests left in the current window after this one; 0 means the next call answers 429 until RateLimit-Reset elapses.
schema:
type: integer
minimum: 0
example: 79
RateLimit-Reset:
description: Seconds until the window turns and the full allowance returns.
schema:
type: integer
minimum: 1
example: 1740
RateLimit-Policy:
description: 'The policy in force as "<limit>;w=<window seconds>": the allowance and the hour it is measured over.'
schema:
type: string
pattern: ^[0-9]+;w=3600$
example: 80;w=3600
schemas:
Problem:
type: object
description: 'Every error answer on this host: a stable machine-readable code in error, a sentence for the human in message, and the missing field names when the code is missing_required_fields.'
required:
- error
properties:
error:
type: string
description: 'Stable code: invalid_json, invalid_request, missing_required_fields, product_not_recognized, product_url_not_detected, session_failed, session_in_checkout, session_cancelled, session_not_found, idempotency_key_reused, idempotency_key_in_progress, rate_limited, invalid_token, token_required, tracking_number_required, tracking_not_found, invalid_month, issue_not_found, no_issues'
message:
type: string
description: What went wrong, in a sentence
missing:
type: array
items:
type: string
description: Field names still needed, with missing_required_fields and product_not_recognized
session_id:
type: string
description: 'With session_in_checkout, session_cancelled or session_not_found: the session the refusal is about (for session_in_checkout, the one whose checkout the human has started)'
external_id:
type: string
description: 'With session_in_checkout: the external_id you sent'
available_months:
type: array
items:
type: string
description: 'With issue_not_found and no_issues: every published price-index month, newest first'
TrackingResult:
type: object
description: 'Tracking for one shipment whose label was bought on smklog.com: the latest status and up to eight scan events, newest first. Never carries names, addresses, emails or order ids.'
required:
- tracking
- events
properties:
tracking:
type: object
required:
- tracking_code
- status
properties:
tracking_code:
type: string
description: The tracking number
carrier:
type: string
description: USPS, UPS, FedEx or DHL
status:
type: string
description: 'Machine status: pre_transit, in_transit, out_for_delivery, available_for_pickup, delivered, return_to_sender, cancelled, failure or unknown'
status_label:
type: string
description: The status in words
status_detail:
type: string
description: The carrier detail of the latest status, empty when none was given
last_event_at:
type: string
description: When the latest scan happened (ISO 8601), empty before the first scan
estimated_delivery:
type: string
description: Estimated delivery date when the carrier gives one, empty otherwise
delivered_at:
type: string
description: When the shipment was delivered, empty until then
updated_at:
type: string
description: When SMKlog last refreshed this record
events:
type: array
maxItems: 8
description: Scan events, newest first; empty before the carrier first scans the package
items:
type: object
properties:
status:
type: string
description: Machine status of this scan, empty when the carrier gave none
status_label:
type: string
description: The status in words
message:
type: string
description: The carrier scan message
datetime:
type: string
description: When the scan happened, as the carrier stamps it
city:
type: string
state:
type: string
zip:
type: string
message:
type: string
description: A sentence about the status for the human
securitySchemes:
clientCredentials:
type: oauth2
description: 'Optional on every operation but upsertPaymentSession, which always needs one. A token scopes external_id (the create-or-update on createPaymentSession and upsertPaymentSession) and the Idempotency-Key header on the payment-session writes; a call that sends neither works without a token. Any agent registers an OAuth 2.0 client_credentials client itself, with no human step, at POST https://quote-api.smklog.com/oauth/register (RFC 7591; such a client runs at the public limits), or asks info@smklog.com for a client with its own larger hourly bucket (https://smklog.com/auth.md); either way it sends the opaque bearer it gets from /oauth/token. Discovery: /.well-known/oauth-authorization-server (registration_endpoint).'
flows:
clientCredentials:
tokenUrl: https://quote-api.smklog.com/oauth/token
scopes:
quote: Price parcels and prepare checkouts from a dedicated per-client rate-limit bucket
externalDocs:
description: 'Developer portal: docs, schemas, playground, rate limits, status'
url: https://smklog.com/developers/
x-provenance:
method: published
publisher: SMKlog (SMKLOG-2022 LLC)
source: https://quote-api.smklog.com/openapi.json
published: '2026-11-07'
x-mcp:
transport: streamable-http
endpoint: https://quote-api.smklog.com/mcp
serverCard: https://quote-api.smklog.com/.well-known/mcp/server-card.json