Normalise any API response into a consistent envelope: ok, data, error, status, meta
Project description
unwrapr
Stop writing the same API response unwrapping code in every project.
pip install unwrapr
Requires Python 3.9+ · Zero dependencies
The problem
Every API you call returns a different shape:
{"data": {...}, "error": null} # some APIs
{"success": True, "result": {...}} # other APIs
{"status": 200, "payload": {...}} # yet others
[{"id": 1}, {"id": 2}] # or just raw lists
You end up writing custom unwrapping logic for every single one.
unwrapr fixes that. One function. Any shape. Consistent output.
Quick start
from unwrapr import unwrap
# Works with any API response shape
env = unwrap({"success": True, "data": {"id": 1, "name": "Alice"}})
env.ok # True
env.data # {"id": 1, "name": "Alice"}
env.error # None
env.status # None
env.meta # {}
Supported shapes
unwrapr auto-detects the response shape and normalises it:
# { data, error }
unwrap({"data": {"id": 1}, "error": None})
# { success, data }
unwrap({"success": True, "result": {"id": 1}})
# { status, payload }
unwrap({"status": 200, "payload": {"id": 1}})
# JSON:API
unwrap({"data": [...], "included": [], "meta": {"total": 5}})
# Plain dict or list
unwrap({"id": 1, "name": "Alice"})
unwrap([1, 2, 3])
HTTP status override
Pass the HTTP status code to let it override the body:
env = unwrap(response.json(), status=response.status_code)
env.ok # True if 2xx, False otherwise
env.status # the actual HTTP status code
Safe data access
env = unwrap({"success": False, "error": "Not found"})
# Raises UnwraprError if not ok
data = env.unwrap()
# Returns default if not ok — never raises
data = env.unwrap_or([])
data = env.unwrap_or(None)
# Use as a boolean
if env:
print(env.data)
Custom strategies
Add your own shape detection logic:
from unwrapr import unwrap, DEFAULT_STRATEGIES
def my_api_strategy(raw):
if isinstance(raw, dict) and "response" in raw:
return {
"ok": raw["response"]["success"],
"data": raw["response"]["body"],
"error": raw["response"].get("error"),
"status": None,
"meta": {},
}
return None # return None to try the next strategy
env = unwrap(response, strategies=[my_api_strategy] + DEFAULT_STRATEGIES)
Works great with petchr
from petchr import petch
from unwrapr import unwrap
resp = petch("https://api.example.com/users/1")
env = unwrap(resp.data, status=resp.status_code)
if env:
print(env.data)
else:
print(f"Error: {env.error}")
The Envelope
Every call returns an Envelope:
| Field | Type | Description |
|---|---|---|
ok |
bool |
True if response is successful |
data |
Any |
The extracted payload |
error |
`str | None` |
status |
`int | None` |
meta |
dict |
Extra fields (pagination, links etc) |
raw |
Any |
Original response before normalising |
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file unwrapr-1.0.0.tar.gz.
File metadata
- Download URL: unwrapr-1.0.0.tar.gz
- Upload date:
- Size: 8.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e023fc28bb91c818398981109841d098b78a8f1d0e50a7fa91d0acd41a3b57f0
|
|
| MD5 |
672474df52d9652a91ab41e12aa0fb68
|
|
| BLAKE2b-256 |
73a59ad1834de0981dab061959f1049cc1a6067c84a8885db37e695f0a56a51e
|
File details
Details for the file unwrapr-1.0.0-py3-none-any.whl.
File metadata
- Download URL: unwrapr-1.0.0-py3-none-any.whl
- Upload date:
- Size: 7.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f00c58682bf2bca5e8f64582f6371482c04adf8f0585b0b04fa4aa4bda7ee36b
|
|
| MD5 |
27f011872ec098bf673e8bab6daf2f28
|
|
| BLAKE2b-256 |
ec72bf9c4d6c5c3fe48fa808d8a0cefd0ff80218a86fce585f211a67f50f27c5
|