Skip to main content

An unofficial Python client library for the Duffel travel booking API

Project description

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

duffelbag-0.1.3.tar.gz (51.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

duffelbag-0.1.3-py3-none-any.whl (33.0 kB view details)

Uploaded Python 3

File details

Details for the file duffelbag-0.1.3.tar.gz.

File metadata

  • Download URL: duffelbag-0.1.3.tar.gz
  • Upload date:
  • Size: 51.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.6.1

File hashes

Hashes for duffelbag-0.1.3.tar.gz
Algorithm Hash digest
SHA256 663fe79292408470dd811607c04279e2dbac15e213d2166645698bcb99647791
MD5 d3f925b6819daf24336149d9b4863a12
BLAKE2b-256 55d4e206586376a53d83cfa27bc022a8018407d1acd6cd3bc8b4da2131bc0229

See more details on using hashes here.

File details

Details for the file duffelbag-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: duffelbag-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 33.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.6.1

File hashes

Hashes for duffelbag-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 12a3214103ed4ff794ed6c0b6ddfa57a79efeb0056e47f03c568ea0d58daa0b4
MD5 a501a4fe5aa73a6536915147d3edc701
BLAKE2b-256 ae891ad6d54235cb334c90b4e896a07992a869c505120d3262a75f9aa1764b63

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page