Skip to main content

duffelbag

An unofficial Python client library for the Duffel travel booking API.

Duffel provides a single API for searching and booking flights across hundreds of airlines, hotel stays, and managing payments. Duffel discontinued support for their own Python library in 2023. While there are other Python libraries that wrap parts of their API, none appear to be complete.

duffelbag aims to provide a comprehensive, typed, Pythonic interface to the API with both synchronous and async client support.

Features

  • Sync and async clients with identical API surfaces
  • Pydantic v2 models for all request and response types
  • Automatic pagination with lazy iterators
  • Built-in rate limit handling (auto-retry on 429)
  • Typed exceptions mapped to HTTP status codes
  • Covers flights, stays, payments, webhooks, and identity APIs

Installation

Install using pip (or better still, uv pip) with:

pip install duffelbag

You can install the very latest version from source directly from GitHub with:

pip install git+https://github.com/nickovs/duffelbag.git

duffelbag is designed to run on Python 3.12 or later, but may run on earlier versions of Python.

Quick start

Sign up at duffel.com and grab your API token from the dashboard. Test tokens start with duffel_test_ and let you make bookings against Duffel's simulated airlines at no cost.

Search for flights

from duffelbag import Duffel

client = Duffel(token="duffel_test_...")

offer_request = client.offer_requests.create(
    slices=[{
        "origin": "LHR",
        "destination": "JFK",
        "departure_date": "2026-06-15",
    }],
    passengers=[{"type": "adult"}],
    cabin_class="economy",
    return_offers=True,
)

for offer in sorted(offer_request.offers, key=lambda o: float(o.total_amount))[:5]:
    print(f"{offer.owner.name:20s}  {offer.total_amount} {offer.total_currency}")

client.close()

Book a flight

from duffelbag import Duffel

client = Duffel(token="duffel_test_...")

# 1. Search
offer_request = client.offer_requests.create(
    slices=[{"origin": "LHR", "destination": "JFK", "departure_date": "2026-06-15"}],
    passengers=[{"type": "adult"}],
    return_offers=True,
)

# 2. Pick an offer
offer = sorted(offer_request.offers, key=lambda o: float(o.total_amount))[0]

# 3. Create an order
order = client.orders.create(
    selected_offers=[offer.id],
    passengers=[{
        "id": offer.passengers[0].id,
        "title": "mr",
        "gender": "m",
        "given_name": "Test",
        "family_name": "Traveller",
        "born_on": "1990-01-01",
        "email": "test@example.com",
        "phone_number": "+442080000000",
    }],
    type="instant",
    payments=[{
        "type": "balance",
        "amount": offer.total_amount,
        "currency": offer.total_currency,
    }],
)

print(f"Booked! Reference: {order.booking_reference}")
client.close()

Async usage

import asyncio
from duffelbag import AsyncDuffel

async def main():
    async with AsyncDuffel(token="duffel_test_...") as client:
        offer_request = await client.offer_requests.create(
            slices=[{"origin": "LHR", "destination": "CDG", "departure_date": "2026-07-01"}],
            passengers=[{"type": "adult"}],
            return_offers=True,
        )

        async for offer in client.offers.list(offer_request.id, sort="total_amount"):
            print(f"{offer.owner.name}  {offer.total_amount} {offer.total_currency}")

asyncio.run(main())

Browse reference data

from duffelbag import Duffel

client = Duffel(token="duffel_test_...")

# Airlines, airports, aircraft, cities — .page() fetches one page
for airline in client.airlines.list(limit=10).page():
    print(f"[{airline.iata_code}] {airline.name}")

# Place search
for place in client.places.suggest("Tokyo"):
    print(f"{place.type}: {place.name} ({place.iata_code})")

client.close()

API coverage

Section Resources
Flights Offer requests, offers, orders, order changes, order cancellations, seat maps, payments, partial offer requests, batch offer requests, airline-initiated changes, airline credits
Stays Search, quotes, bookings, accommodation, brands, negotiated rates, loyalty programmes
Payments Cards (PCI-compliant endpoint), 3D Secure sessions
Notifications Webhooks, webhook events, webhook deliveries
Identity Customer users, customer user groups, component client keys
Reference data Airlines, airports, aircraft, cities, places, loyalty programmes

Every resource is available through both the sync Duffel client and the async AsyncDuffel client.

Pagination

List endpoints return lazy iterators that fetch pages on demand:

# Iterate through all results automatically
for airline in client.airlines.list():
    print(airline.name)

# Or fetch one page at a time
iterator = client.airports.list(limit=50, iata_country_code="US")
first_page = iterator.page()
second_page = iterator.page()

Async pagination works with async for:

async for offer in client.offers.list(offer_request_id):
    print(offer.total_amount)

Error handling

API errors are raised as typed exceptions:

from duffelbag import Duffel, NotFoundError, ValidationError, AuthenticationError

client = Duffel(token="duffel_test_...")

try:
    client.orders.get("ord_nonexistent")
except NotFoundError as e:
    print(f"Not found: {e}")
except ValidationError as e:
    print(f"Validation error: {e}")
    for error in e.errors:
        print(f"  {error.code}: {error.message}")
except AuthenticationError:
    print("Check your API token")

Examples

See the examples/ directory for runnable scripts:

Set your token via environment variable or a test_api_key file:

export DUFFEL_TOKEN="duffel_test_..."
python examples/search_flights.py

Duffel API documentation

Development

# Clone and install
git clone https://github.com/nickovs/duffelbag.git
cd duffelbag
uv pip install -e ".[dev]"

# Run tests (mocked, no API key needed)
.venv/bin/pytest tests/ --ignore=tests/test_live.py -v

# Run live integration tests (requires test_api_key file)
.venv/bin/pytest tests/test_live.py -v

# Lint
.venv/bin/ruff check src/ tests/

License

MIT

Metadata

Release files for duffelbag 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for duffelbag 0.1.3
File Size Uploaded Provenance
duffelbag-0.1.3.tar.gz 51.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for duffelbag 0.1.3
File Interpreter ABI Platform
duffelbag-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 84.1 kB

Release files / duffelbag-0.1.3.tar.gz

Download URL duffelbag-0.1.3.tar.gz
Size 51.1 kB
Tags Source
SHA-256 checksum
How to use checksums
663fe79292408470dd811607c04279e2dbac15e213d2166645698bcb99647791
BLAKE2b-256 checksum
How to use checksums
55d4e206586376a53d83cfa27bc022a8018407d1acd6cd3bc8b4da2131bc0229
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.6.1

Release files / duffelbag-0.1.3-py3-none-any.whl

Download URL duffelbag-0.1.3-py3-none-any.whl
Size 33.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
12a3214103ed4ff794ed6c0b6ddfa57a79efeb0056e47f03c568ea0d58daa0b4
BLAKE2b-256 checksum
How to use checksums
ae891ad6d54235cb334c90b4e896a07992a869c505120d3262a75f9aa1764b63
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.6.1

Security for duffelbag 0.1.3

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.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page