Unofficial Python SDK for the Papa John's ordering API
Project description
pajohns — Unofficial Papa John's Python SDK
An unofficial, strongly-typed Python SDK for the Papa John's ordering API. Built with pydantic and requests, designed after the patterns of Stripe and OpenAI SDKs.
Disclaimer: This is an unofficial SDK and is not affiliated with, maintained, authorized, endorsed, or sponsored by Papa John's International, Inc. or any of its affiliates. Use at your own risk and in compliance with Papa John's Terms of Service.
Features
- Object-Oriented API — Clean, module-based interface (
client.store,client.deals,client.cart, etc.) - Strong Typing — Full Pydantic v2 request/response models with IDE auto-completion
- Session Management — Optional file-based session persistence for local scripts
- Cookie-Based Auth — Import a live browser session in one line
- OrderingSkill — High-level conversational ordering interface for AI agents
- Bilingual Output — English/Chinese display support in OrderingSkill
Installation
pip install pajohns
Requirements: Python 3.12+
Authentication
pajohns authenticates by importing your browser session cookies. No username/password is stored.
How to get your cookie string:
- Log in to papajohns.com in Chrome
- Open DevTools → Application → Cookies →
https://www.papajohns.com - Copy all cookies as a header string (or use a browser extension like "Copy Cookies")
from pajohns import PajohnsClient
client = PajohnsClient(session_file="session.json") # persist session to disk
client.login("pj-session-id=abc123; auth-store=%7B%22state%22...")
After calling login(), the session is saved to session_file and reloaded automatically on future runs.
Quick Start
from pajohns import PajohnsClient
# Initialize (loads existing session from disk if file exists)
client = PajohnsClient(session_file="session.json")
# Search for nearby stores
stores = client.store.search("10001")
for s in stores:
print(f"#{s.store_id} — {s.address.address1}, {s.address.city}")
# Set the store you want to order from
client.session_data["store_id"] = stores[0].store_id
# Browse available deals
deals = client.deals.get_by_store(stores[0].store_id)
for d in deals:
print(f"[{d.deal_id}] {d.title} — ${d.display_price}")
# Add a deal to cart (uses default configuration)
client.cart.add_deal(deal_id=61891, quantity=1)
# Generate a PayPal payment link to complete the order
url = client.checkout.get_paypal_link()
print(f"Pay here: {url}")
API Reference
PajohnsClient
PajohnsClient(session_file: str | None = None)
The main entry point. Pass session_file to enable automatic session persistence.
| Method | Description |
|---|---|
client.login(cookie_str) |
Import a browser cookie string and authenticate |
client.save_session() |
Persist current session state to disk |
client.store — Store Discovery
# Search by ZIP code (carryout)
stores = client.store.search("90210")
# Search delivery stores by address
result = client.store.get_delivery_stores(
street="123 Main St", city="Los Angeles", postal_code="90210", state="CA"
)
# Get store details by ID
store = client.store.get_details(store_id=3888)
# Get available order timeslots
slots = client.store.get_timeslots(store_id=3888, fulfillment_type="CARRYOUT")
client.deals — Deals & Specials
# List all deals for a store
deals = client.deals.get_by_store(store_id=3888)
# Get configuration for a specific deal
config = client.deals.get_deal(deal_id=61891)
# Fetch all ordering context in one call (toppings, sauces, crusts, instructions)
ctx = client.deals.get_deal_context(deal_id=61891)
print(ctx.deal_config.title)
print([s.title for s in ctx.sauces])
print(ctx.premium_crusts)
client.cart — Cart Management
# Add a deal with default options
cart = client.cart.add_deal(deal_id=61891)
# Add a deal with custom pizza configuration
cart = client.cart.add_deal(
deal_id=61891,
quantity=1,
options={
"products": [
{
"sku": "PIZZA-1-4-1",
"productGroupId": "classic-pepperoni",
"sauceId": 429,
"sectionWhole": {"toppings": [19, 20]},
"instructions": [
{"groupId": 3, "detailId": 58}, # Normal Bake
{"groupId": 4, "detailId": 10}, # Normal Cut
],
"productModificationCodes": [],
"sides": [],
"papaSized": False,
"productConfigurationId": 0,
"quantity": 1,
}
]
}
)
# Sync cart state with server
cart = client.cart.refresh()
# Remove an item
client.cart.remove(shopcart_item_id="abc-123")
# Clear cart locally
client.cart.empty()
client.checkout — Payment
# Generate a PayPal checkout link
url = client.checkout.get_paypal_link()
client.catalog — Menu & Ingredients
# Get available toppings for a store
toppings = client.catalog.get_toppings(store_id=3888, topping_ids=[19, 20, 21])
# Get available sauces
sauces = client.catalog.get_sauces(store_id=3888)
# Get premium crust options and flavor upgrades
crust_data = client.catalog.get_crusts(store_id=3888)
print(crust_data.crust_types) # {size_key: [PremiumCrust, ...]}
print(crust_data.crust_flavors) # {size_key: [CrustFlavor, ...]}
# Get nutritional info for a product group
info = client.catalog.get_nutritional_info(product_group_id="classic-pepperoni")
client.tracking — Order Tracking
status = client.tracking.get_order_status(order_id="PJ-123456")
client.ordering — AI Agent OrderingSkill
OrderingSkill is a high-level interface that wraps the full ordering state machine. All methods return formatted strings suitable for presenting directly to a user or AI agent.
Lifecycle: Authentication → Store → Deals → Pizza Configuration → Cart → Payment
# Phase 0: Auth check (prompts for cookies if not authenticated)
print(client.ordering.get_or_confirm_store())
# Phase 0: Login
print(client.ordering.login("pj-session-id=abc; auth-store=..."))
# Phase 1: Store
print(client.ordering.search_stores("10001"))
print(client.ordering.set_store(store_id=3888, order_type="CARRYOUT"))
# Phase 2: Deals
print(client.ordering.get_all_deals())
# Phase 2: Switch order type
print(client.ordering.set_order_type("DELIVERY"))
# Phase 3: Select a deal and load configuration
print(client.ordering.select_deal(deal_id=61891))
# Phase 4: View pizza configuration options
print(client.ordering.get_pizza_options(pizza_index=0))
# Phase 4: Configure a pizza
print(client.ordering.configure_pizza(
pizza_index=0,
config={
"crust_type_id": 1, # Original Crust
"sauce_id": 429, # Original Pizza Sauce
"topping_ids": [19, 20],
"instructions": [
{"groupId": 3, "detailId": 58}, # Normal Bake
{"groupId": 4, "detailId": 10}, # Normal Cut
],
}
))
# Phase 4: Look up a specific option
print(client.ordering.explain_option("Thin Crust"))
# Phase 5: Add configured pizzas to cart
print(client.ordering.add_to_cart())
# Phase 6: Generate payment link
print(client.ordering.get_payment_link())
# Utility: Order summary
print(client.ordering.get_order_summary())
Exceptions
All exceptions inherit from PajohnsException.
from pajohns import APIError, AuthError, NetworkError, ValidationError
try:
client.cart.add_deal(deal_id=99999)
except AuthError:
print("Session expired — please log in again")
except APIError as e:
print(f"API error {e.status_code}: {e}")
except NetworkError:
print("Network request failed")
except ValidationError:
print("Invalid input data")
| Exception | When raised |
|---|---|
NetworkError |
Timeout, connection failure |
APIError |
Non-2xx HTTP response or business logic error |
AuthError |
Expired session, missing credentials |
ValidationError |
Client-side validation failure |
Session File Format
When session_file is set, the client saves session state as JSON:
{
"cookies": {"pj-session-id": "...", "auth-store": "..."},
"store_id": 3888,
"order_type": "CARRYOUT",
"customerToken": "...",
"shopcart_id": "...",
"cart_state": {...},
"cart_price": {...}
}
You can pre-populate store_id and order_type in this file to skip interactive setup.
Disclaimer
This SDK is not affiliated with Papa John's International, Inc. It reverse-engineers the public-facing web API for personal and educational use. The API may change at any time without notice, which could break SDK functionality. The author assumes no responsibility for any issues arising from its use.
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 pajohns-0.1.0.tar.gz.
File metadata
- Download URL: pajohns-0.1.0.tar.gz
- Upload date:
- Size: 32.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ac876a9ad2778787bd01a7a290e4c4bd0a1f1093ed1b225594ea688eea87d79a
|
|
| MD5 |
e1b64e3f9ae72fe25d8e75018a20c072
|
|
| BLAKE2b-256 |
1c30e52b6a30035103764acef7b031620dba3ac3fe7340b515fd8f2d9a33d994
|
File details
Details for the file pajohns-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pajohns-0.1.0-py3-none-any.whl
- Upload date:
- Size: 42.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
75aad6bbe123cb0b34b649a55ad5c6ba667083b18e8ec3ed3d32fcf0ee27940f
|
|
| MD5 |
f03a5177198a01fdfb483747e5cadd8f
|
|
| BLAKE2b-256 |
92d60c70ca4bad87499276b0569b463871d29187de3bbb82bbd7d5eecb2b39fc
|