API Usage Guide¶
This guide covers how to work with the litestar-pydotorg REST API.
Overview¶
The Python.org API provides programmatic access to:
Python Releases - Download information, version history, and release files
Job Board - Python job postings with search and filtering
Events - Community events, calendars, and locations
Content - Blog posts, success stories, and community content
Sponsors - PSF sponsors and sponsorship information
Users - User management and authentication
Base URL and Versioning¶
All API endpoints are available at:
https://python.org/api/v1/
For local development:
http://localhost:8000/api/v1/
The API is versioned using URL path prefixes. Currently, v1 is the active version.
API Documentation¶
Interactive API documentation is available at:
UI |
URL |
Description |
|---|---|---|
Scalar |
|
Modern, feature-rich API explorer (recommended) |
Swagger |
|
Classic OpenAPI UI |
ReDoc |
|
Clean, responsive documentation |
Making Requests¶
Basic GET Request¶
curl "http://localhost:8000/api/v1/releases/latest"
Python Example¶
import httpx
response = httpx.get("http://localhost:8000/api/v1/releases/latest")
release = response.json()
print(f"Latest Python: {release['name']}")
print(f"Released: {release['release_date']}")
JavaScript Example¶
const response = await fetch("http://localhost:8000/api/v1/releases/latest");
const release = await response.json();
console.log(`Latest Python: ${release.name}`);
Authentication¶
Most read endpoints are public, but write operations require authentication. See the Authentication Guide for complete details.
Quick Authentication¶
# Login and get tokens
curl -X POST http://localhost:8000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "your_username", "password": "your_password"}'
# Use the access token
curl http://localhost:8000/api/auth/me \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Pagination¶
The API supports two pagination patterns:
Limit/Offset (Most Endpoints)¶
# Get results 21-40
curl "http://localhost:8000/api/v1/jobs?limit=20&offset=20"
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
integer |
100 |
Maximum items to return (1-1000) |
|
integer |
0 |
Number of items to skip |
Page-Based (Some Endpoints)¶
# Get page 2 with 20 items per page
curl "http://localhost:8000/api/v1/releases?currentPage=2&pageSize=20"
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
integer |
1 |
Page number (1-indexed) |
|
integer |
20 |
Items per page |
Error Handling¶
The API uses standard HTTP status codes:
Code |
Meaning |
Example Scenario |
|---|---|---|
200 |
Success |
Request completed successfully |
201 |
Created |
Resource created successfully |
400 |
Bad Request |
Invalid request body or parameters |
401 |
Unauthorized |
Missing or invalid authentication |
403 |
Forbidden |
Authenticated but lacking permissions |
404 |
Not Found |
Resource does not exist |
422 |
Unprocessable Entity |
Validation error in request data |
429 |
Too Many Requests |
Rate limit exceeded |
500 |
Internal Server Error |
Server-side error |
Error Response Format¶
{
"detail": "Human-readable error message",
"status_code": 404,
"extra": {}
}
Handling Errors¶
import httpx
response = httpx.get("http://localhost:8000/api/v1/jobs/nonexistent-id")
if response.status_code == 200:
job = response.json()
elif response.status_code == 404:
print("Job not found")
elif response.status_code == 401:
print("Authentication required")
elif response.status_code == 429:
retry_after = response.headers.get("Retry-After", 60)
print(f"Rate limited. Retry after {retry_after} seconds")
else:
error = response.json()
print(f"Error: {error['detail']}")
Rate Limiting¶
The API implements rate limiting to ensure fair usage.
Rate Limit Tiers¶
Tier |
Anonymous |
Authenticated |
Staff |
|---|---|---|---|
CRITICAL |
5/min |
20/min |
100/min |
HIGH |
20/min |
60/min |
300/min |
MEDIUM |
60/min |
180/min |
900/min |
LOW |
120/min |
300/min |
1500/min |
Checking Your Quota¶
Rate limit information is included in response headers:
RateLimit-Policy: 120;w=60
RateLimit-Remaining: 118
RateLimit-Reset: 1735689600
RateLimit-Limit: 120
Handling Rate Limits¶
import time
import httpx
def request_with_retry(url: str, max_retries: int = 3) -> httpx.Response:
"""Make request with exponential backoff for rate limits."""
for attempt in range(max_retries):
response = httpx.get(url)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 60))
wait_time = retry_after * (2 ** attempt)
print(f"Rate limited. Waiting {wait_time} seconds...")
time.sleep(wait_time)
continue
return response
raise Exception("Max retries exceeded")
API Endpoints¶
Authentication¶
Method |
Endpoint |
Description |
|---|---|---|
POST |
|
Create new account |
POST |
|
JWT login |
POST |
|
Refresh JWT tokens |
POST |
|
Logout (invalidate tokens) |
POST |
|
Get current user info |
Downloads¶
Method |
Endpoint |
Description |
|---|---|---|
GET |
|
List all releases |
GET |
|
Get latest Python 3 release |
GET |
|
Get latest for version (2, 3) |
GET |
|
Get release by ID |
GET |
|
List files for a release |
Jobs¶
Method |
Endpoint |
Description |
|---|---|---|
GET |
|
List all jobs |
GET |
|
Get job by ID |
POST |
|
Search jobs with filters |
GET |
|
List job types |
GET |
|
List job categories |
Events¶
Method |
Endpoint |
Description |
|---|---|---|
GET |
|
List events |
GET |
|
Get upcoming events |
GET |
|
Get featured events |
GET |
|
Get event by ID |
Search¶
Method |
Endpoint |
Description |
|---|---|---|
POST |
|
Full-text search |
GET |
|
Autocomplete suggestions |
Client SDKs¶
Generating Python Client¶
pip install openapi-python-client
openapi-python-client generate \
--url http://localhost:8000/api/openapi.json \
--output-path ./python-org-client
Generating TypeScript Client¶
npx openapi-typescript http://localhost:8000/api/openapi.json \
--output ./types/python-org-api.ts
Best Practices¶
Caching¶
Respect Cache-Control and ETag headers:
etag = None
cached_data = None
def get_releases():
global etag, cached_data
headers = {}
if etag:
headers["If-None-Match"] = etag
response = httpx.get(
"http://localhost:8000/api/v1/releases",
headers=headers
)
if response.status_code == 304:
return cached_data
etag = response.headers.get("ETag")
cached_data = response.json()
return cached_data
Request Batching¶
Use list endpoints instead of individual requests:
# Instead of multiple single requests
# for id in ids:
# httpx.get(f"/api/v1/jobs/{id}")
# Use list endpoint with filtering
response = httpx.get(
"http://localhost:8000/api/v1/jobs",
params={"limit": 100}
)
Error Retry Strategy¶
import httpx
from httpx import HTTPTransport
transport = HTTPTransport(retries=3)
client = httpx.Client(transport=transport)
response = client.get("http://localhost:8000/api/v1/releases")
Testing with the API¶
Using HTTPie¶
pip install httpie
# Get latest release
http http://localhost:8000/api/v1/releases/latest
# Search jobs
http POST http://localhost:8000/api/v1/jobs/search query="django" limit:=10
# Authenticated request
http http://localhost:8000/api/auth/me "Authorization:Bearer $TOKEN"
Using Postman¶
Open Postman
Click Import
Select Link tab
Enter:
http://localhost:8000/api/openapi.jsonClick Continue and then Import
See Also¶
Authentication Guide - Detailed authentication flows
Rate Limiting Architecture - Rate limiting details
API Tags Structure - API organization