smklog-sdk (Python)
Official Python client and command line for the SMKlog Quote API: live USPS, UPS, FedEx and DHL Express parcel rates from a US origin, a checkout handed to a human, and where that checkout stands. No API key, no account, no dependencies beyond the standard library. Python 3.9 or newer.
pip install smklog-sdk
The distribution is smklog-sdk; the import package is smklog.
Quickstart
from smklog import SmklogClient
client = SmklogClient()
quote = client.get_parcel_quote(product="Yamaha FG800 acoustic guitar in a hard case",
from_postal_code="07102", to_postal_code="30303")
for rate in quote.rates: # best value first, as the API returns them
print(rate.carrier, rate.service, rate.amount, "counter", rate.retail_amount)
if quote.is_priced() and quote.quote_id: # the handle is good for 15 minutes
best = quote.cheapest()
session = client.create_payment_session(quote_id=quote.quote_id, service=best.service)
print(session.handoff_url) # the human confirms and pays here
if session.session_id: # None only when the record was not stored
print(client.get_checkout_status(session.session_id).status)
The pair costs one carrier call: the quote answer carries quote_id, and a checkout
opened with it reuses those rates (session.quote_reused is true) instead of pricing
the shipment again.
Exact measurements skip the box estimate: pass all four of length_cm, width_cm,
height_cm, weight_kg (the client marks the package manual). A request can also be a
QuoteRequest / CheckoutSessionRequest model or a plain dict; fields keep the wire names.
Every get_parcel_quote call spends a live carrier rating on the server: one call per
shipment, cache on your side, never poll. create_payment_session takes one of two forms:
create_payment_session(quote_id=..., service=...): the quote is reused and no second carrier call is spent.quote_idis the handle every priced quote answer carries; it isNonewhen nothing was priced or the handle could not be stored. The API ignores any shipment fields sent next to a validquote_id.create_payment_session(product=..., from_zip=..., to_zip=..., to_country=..., quantity=..., service=...): the API prices the shipment live and opens the checkout in one call.
A quote_id older than 15 minutes makes the API price the shipment fields live, so send
them along when the quote may have aged. The client never turns one form into the other.
Client options
| Option | Default | Meaning |
|---|---|---|
base_url |
https://quote-api.smklog.com |
The API host. |
timeout |
30.0 |
Seconds per request; a quote can take 10-20 s while carriers answer. |
token |
None |
Optional OAuth bearer for a dedicated rate-limit bucket (auth.md); sent as Authorization: Bearer ... only when set. |
user_agent |
None |
Suffix appended after smklog-sdk-python/0.1.0. |
Operations: get_parcel_quote(...) -> QuoteResponse (with quote_id, rates,
cheapest(), is_priced()), create_payment_session(...) -> CheckoutSession,
get_checkout_status(session_id) -> CheckoutStatus, status() -> Liveness.
Every model is a dataclass whose fields are the wire names. Fields this version does
not know about land in .extra; .raw is the body exactly as received; .to_dict()
serializes back. Only the idempotent GETs (get_checkout_status, status) are retried,
at most twice, on network failures and 5xx. POSTs are never retried, and a 429 is never
retried anywhere: the buckets are hourly (about 80 quotes, 60 status reads, 20 token
requests per client), so a retry loop only burns them faster.
Errors
Every failure raises SmklogError or one of its subclasses, carrying status (HTTP
status, 0 when nothing was sent), error (the stable code), message, missing
(field names, often empty) and body (the response body as received). When the Problem
carries no message, the error's message is the code plus the missing fields
(missing_required_fields: missing to_postal_code). Errors pickle, so they cross a
multiprocessing boundary intact.
| Code | Exception |
|---|---|
rate_limited, slow_down |
RateLimitedError |
product_not_recognized, product_url_not_detected, missing_required_fields, invalid_request, invalid_json |
InvalidRequestError |
session_not_found |
NotFoundError |
session_failed |
SessionFailedError |
| anything else | SmklogError |
A network failure or timeout is SmklogError with status == 0 and
error == "network_error"; the native exception is its __cause__. Requests the client
refuses before sending (a 4-digit ZIP, a country outside US/CA/GB/DE/AU, a quantity
below 1, measurements that do not come as a set of four) raise InvalidRequestError
with status == 0.
Command line
smklog quote "Yamaha FG800 acoustic guitar in a hard case" 07102 30303 [--country US] [--qty 1] [--dims 112x48x20 --kg 6.4] [--json]
smklog session --quote-id q_... [--service "USPS Ground Advantage"] [--json]
smklog session-status as_... [--json]
smklog status [--json]
Human output is a small table; --json prints the raw answer. A priced smklog quote
also prints the quote_id on its own line, ready to pass to smklog session --quote-id
within 15 minutes (--json carries it in the body):
$ smklog quote "Yamaha FG800 acoustic guitar in a hard case" 07102 30303
Yamaha FG800 acoustic guitar in a hard case
Box: 112 x 48 x 20 cm, 6.4 kg (estimate, medium confidence)
Carrier Service Checkout Counter Delivery
USPS Ground Advantage $38.42 $46.95 2-5 business days
UPS Ground $41.10 $67.35 3 days, by 2026-10-16
quote_id q_8f3c2e1a4b7d
$ smklog session --quote-id q_8f3c2e1a4b7d --service "USPS Ground Advantage"
Exit code 0 on a priced answer or a successful read, 2 when the API refuses (the Problem
is printed) or answers a quote without a price, 3 on a rate limit, 1 when the host could
not be reached.
--base-url and --token (or SMKLOG_BASE_URL / SMKLOG_TOKEN) are accepted by every
command. python -m smklog runs the same program.
Development
cd python
python3 -m unittest discover -s tests
The tests replay the vendored examples in ../testdata/ over a local HTTP server and
never reach the network. SMKLOG_LIVE=1 adds one live test that calls status() on
production; nothing in the suite ever requests a quote.
Docs, schemas, playground and status page: smklog.com/developers. The contract is the OpenAPI 3.1 document at quote-api.smklog.com/openapi.json.
MIT license; see LICENSE.
Metadata
Release files for smklog-sdk 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| smklog_sdk-0.1.0.tar.gz | 26.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| smklog_sdk-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 44.9 kB
Release files / smklog_sdk-0.1.0.tar.gz
| Download URL | smklog_sdk-0.1.0.tar.gz |
|---|---|
| Size | 26.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c746502710e5489e63d44ff19b4f8232dde54b72ab279116880d43982d48092b
|
|
BLAKE2b-256 checksum How to use checksums |
b984d215ff947d5532c2ef68f40e0cd024a196443504d9bdde796f014d351322
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / smklog_sdk-0.1.0-py3-none-any.whl
| Download URL | smklog_sdk-0.1.0-py3-none-any.whl |
|---|---|
| Size | 18.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bd35683d93b63493b906502cb7b7567938dbe3b7f82d86960af02f5664474c6d
|
|
BLAKE2b-256 checksum How to use checksums |
e22084223c6151613d44916f41f94cbb865fde4212783d9c6dea82236b59af8c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Security for smklog-sdk 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.