API Getting Started Guide¶
Welcome to the Python.org API. This guide covers everything you need to start building applications that integrate with Python.org.
Introduction¶
What the API Provides¶
The Python.org API gives you 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/
The API is versioned using URL path prefixes. Currently, v1 is the active version.
API Documentation UIs¶
Interactive API documentation is available at:
UI |
URL |
Description |
|---|---|---|
Scalar |
|
Modern, feature-rich API explorer (recommended) |
Swagger |
|
Classic OpenAPI UI |
ReDoc |
|
Clean, responsive documentation |
Visit these endpoints in your browser to explore the full API schema and try requests interactively.
Authentication¶
Most read endpoints are public, but write operations require authentication. The API supports JWT (JSON Web Tokens) for stateless authentication.
Quick Start with JWT Tokens¶
Step 1: Get a Token
curl -X POST https://python.org/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "your_username", "password": "your_password"}'
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 604800
}
Step 2: Use the Token
Include the access token in the Authorization header for authenticated requests:
curl https://python.org/api/auth/me \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Token Lifetimes¶
Token Type |
Lifetime |
Purpose |
|---|---|---|
Access Token |
7 days |
API access |
Refresh Token |
30 days |
Renew access tokens |
Refreshing Tokens¶
When your access token expires, use the refresh token to get new tokens:
curl -X POST https://python.org/api/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token": "your_refresh_token"}'
For comprehensive authentication documentation, see API Authentication Guide.
Making Your First Request¶
Example: Get List of Jobs¶
Fetch approved Python job postings:
cURL¶
curl "https://python.org/api/v1/jobs?status=approved&limit=10"
Python (requests)¶
import requests
response = requests.get(
"https://python.org/api/v1/jobs",
params={"status": "approved", "limit": 10}
)
jobs = response.json()
for job in jobs:
print(f"{job['job_title']} at {job['company_name']}")
Python (httpx - async)¶
import httpx
async def get_jobs():
async with httpx.AsyncClient() as client:
response = await client.get(
"https://python.org/api/v1/jobs",
params={"status": "approved", "limit": 10}
)
return response.json()
# Run with: asyncio.run(get_jobs())
JavaScript (fetch)¶
const response = await fetch(
"https://python.org/api/v1/jobs?status=approved&limit=10"
);
const jobs = await response.json();
jobs.forEach(job => {
console.log(`${job.job_title} at ${job.company_name}`);
});
Example: Get a Specific Release¶
Fetch details about the latest Python release:
cURL¶
curl "https://python.org/api/v1/releases/latest"
Python¶
import requests
response = requests.get("https://python.org/api/v1/releases/latest")
release = response.json()
print(f"Latest Python: {release['name']}")
print(f"Released: {release['release_date']}")
print(f"Download: {release['resource_uri']}")
JavaScript¶
const response = await fetch("https://python.org/api/v1/releases/latest");
const release = await response.json();
console.log(`Latest Python: ${release.name}`);
console.log(`Released: ${release.release_date}`);
Example: Search Jobs with Filters¶
import requests
response = requests.post(
"https://python.org/api/v1/jobs/search",
json={
"query": "django",
"location": "remote",
"job_type_ids": ["uuid-for-full-time"]
}
)
matching_jobs = response.json()
Common Patterns¶
Pagination¶
The API supports two pagination patterns depending on the endpoint:
Pattern 1: Limit/Offset (Most Endpoints)¶
# Get results 21-40
curl "https://python.org/api/v1/jobs?limit=20&offset=20"
Parameters:
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
integer |
100 |
Maximum items to return (1-1000) |
|
integer |
0 |
Number of items to skip |
Example (Python):
import requests
def get_all_jobs():
"""Fetch all jobs using pagination."""
all_jobs = []
offset = 0
limit = 100
while True:
response = requests.get(
"https://python.org/api/v1/jobs",
params={"limit": limit, "offset": offset, "status": "approved"}
)
jobs = response.json()
if not jobs:
break
all_jobs.extend(jobs)
offset += limit
if len(jobs) < limit:
break
return all_jobs
Pattern 2: Page-Based (Some List Endpoints)¶
# Get page 2 with 20 items per page
curl "https://python.org/api/v1/releases?currentPage=2&pageSize=20"
Parameters:
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
integer |
1 |
Page number (1-indexed) |
|
integer |
20 |
Items per page |
Filtering and Searching¶
Most list endpoints support filtering via query parameters:
# Filter jobs by status
curl "https://python.org/api/v1/jobs?status=approved"
# Filter releases by Python version
curl "https://python.org/api/v1/releases/latest/3"
# Filter events by upcoming
curl "https://python.org/api/v1/events?upcoming=true"
For complex searches, use the search endpoints:
# Full-text search across content
response = requests.post(
"https://python.org/api/v1/search",
json={
"query": "asyncio tutorial",
"indexes": ["pages", "blogs"],
"limit": 20
}
)
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 (Python):
import requests
response = requests.get("https://python.org/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']}")
Handling Errors (JavaScript):
try {
const response = await fetch("https://python.org/api/v1/jobs/nonexistent-id");
if (!response.ok) {
if (response.status === 404) {
console.log("Job not found");
} else if (response.status === 429) {
const retryAfter = response.headers.get("Retry-After") || 60;
console.log(`Rate limited. Retry after ${retryAfter} seconds`);
} else {
const error = await response.json();
console.log(`Error: ${error.detail}`);
}
return;
}
const job = await response.json();
} catch (err) {
console.error("Network error:", err);
}
Rate Limiting¶
The API implements rate limiting to ensure fair usage and protect against abuse.
Rate Limit Tiers¶
The API uses a 4-tier rate limiting system based on endpoint sensitivity:
Tier |
Anonymous |
Authenticated |
Staff |
Use Cases |
|---|---|---|---|---|
CRITICAL |
5/min |
20/min |
100/min |
Auth, search, submissions |
HIGH |
20/min |
60/min |
300/min |
Forms, job/event posts |
MEDIUM |
60/min |
180/min |
900/min |
Community content |
LOW |
120/min |
300/min |
1500/min |
Read-only operations |
Authenticated users receive 4x the anonymous limit. Staff users receive 5x the authenticated limit.
Checking Your Quota¶
Rate limit information is included in response headers:
RateLimit-Policy: 120;w=60 # 120 requests per 60 seconds
RateLimit-Remaining: 118 # 118 requests remaining
RateLimit-Reset: 1735689600 # Unix timestamp for reset
RateLimit-Limit: 120 # Total requests allowed
Handling 429 Responses¶
When rate limited, you receive a 429 Too Many Requests response:
{
"detail": "Rate limit exceeded. Try again in 45 seconds.",
"status_code": 429
}
Response Headers:
Retry-After: 45 # Seconds until reset
Best Practice: Implement Exponential Backoff
import time
import requests
def request_with_retry(url, max_retries=3):
"""Make request with exponential backoff for rate limits."""
for attempt in range(max_retries):
response = requests.get(url)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 60))
wait_time = retry_after * (2 ** attempt) # Exponential backoff
print(f"Rate limited. Waiting {wait_time} seconds...")
time.sleep(wait_time)
continue
return response
raise Exception("Max retries exceeded")
API Endpoints Overview¶
Quick Reference by Domain¶
Domain |
Base Path |
Key Endpoints |
|---|---|---|
Authentication |
|
|
Users |
|
|
Releases |
|
|
Jobs |
|
|
Events |
|
|
Blogs |
|
|
Sponsors |
|
|
Search |
|
|
Pages |
|
|
Downloads Domain¶
GET /api/v1/os/ # List operating systems
GET /api/v1/os/{id} # Get OS by ID
GET /api/v1/os/slug/{slug} # Get OS by slug
GET /api/v1/releases/ # List all releases
GET /api/v1/releases/{id} # Get release by ID
GET /api/v1/releases/slug/{slug} # Get release by slug
GET /api/v1/releases/latest # Get latest Python 3 release
GET /api/v1/releases/latest/{ver} # Get latest for version (2, 3)
GET /api/v1/releases/published # List published releases
GET /api/v1/releases/download-page # Releases for download page
GET /api/v1/files/{id} # Get release file by ID
GET /api/v1/files/release/{id} # List files for a release
GET /api/v1/files/os/{id} # List files for an OS
Jobs Domain¶
GET /api/v1/jobs/ # List all jobs
GET /api/v1/jobs/{id} # Get job by ID
POST /api/v1/jobs/search # Search jobs with filters
GET /api/v1/jobs/mine # List user's jobs (auth required)
GET /api/v1/job-types/ # List job types
GET /api/v1/job-types/{id} # Get job type by ID
GET /api/v1/job-categories/ # List job categories
GET /api/v1/job-categories/{id} # Get job category by ID
Events Domain¶
GET /api/v1/events/ # List events
GET /api/v1/events/{id} # Get event by ID
GET /api/v1/events/upcoming # Get upcoming events
GET /api/v1/events/featured # Get featured events
GET /api/v1/calendars/ # List calendars
GET /api/v1/event-categories/ # List event categories
GET /api/v1/event-locations/ # List event locations
GET /api/v1/event-occurrences/ # List event occurrences
Search Endpoints¶
POST /api/v1/search/ # Full-text search
GET /api/v1/search/autocomplete # Autocomplete suggestions
Full OpenAPI Specification¶
The complete API specification is available at:
GET /api/openapi.json
This JSON schema can be used with OpenAPI tools for client generation, testing, and documentation.
SDKs and Tools¶
OpenAPI Specification¶
The OpenAPI 3.0 specification is available at:
https://python.org/api/openapi.json
You can use this specification with various tools:
Postman: Import the spec for API testing
Swagger Codegen: Generate client SDKs
OpenAPI Generator: Generate clients in 50+ languages
Generating Client SDKs¶
Python Client (using openapi-python-client)¶
pip install openapi-python-client
openapi-python-client generate \
--url https://python.org/api/openapi.json \
--output-path ./python-org-client
TypeScript Client (using openapi-typescript)¶
npx openapi-typescript https://python.org/api/openapi.json \
--output ./types/python-org-api.ts
Go Client (using oapi-codegen)¶
go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@latest
oapi-codegen -package pythonorg \
https://python.org/api/openapi.json > pythonorg/client.go
Postman Collection¶
A Postman collection is available for quick API exploration:
Open Postman
Click Import
Select Link tab
Enter:
https://python.org/api/openapi.jsonClick Continue and then Import
The collection includes all endpoints with example requests.
Testing with HTTPie¶
HTTPie is a user-friendly command-line HTTP client:
# Install
pip install httpie
# Get latest release
http https://python.org/api/v1/releases/latest
# Search jobs
http POST https://python.org/api/v1/jobs/search query="django" limit:=10
# Authenticated request
http https://python.org/api/auth/me "Authorization:Bearer $TOKEN"
Best Practices¶
Caching¶
The API returns caching headers. Respect Cache-Control and ETag headers to reduce unnecessary requests:
import requests
# Store ETag from previous request
etag = None
cached_data = None
def get_releases():
global etag, cached_data
headers = {}
if etag:
headers["If-None-Match"] = etag
response = requests.get(
"https://python.org/api/v1/releases",
headers=headers
)
if response.status_code == 304:
return cached_data # Use cached version
etag = response.headers.get("ETag")
cached_data = response.json()
return cached_data
Request Batching¶
When fetching multiple resources, batch your requests where possible:
# Instead of multiple single requests:
# requests.get("/api/v1/jobs/1")
# requests.get("/api/v1/jobs/2")
# requests.get("/api/v1/jobs/3")
# Use list endpoint with filtering:
response = requests.get(
"https://python.org/api/v1/jobs",
params={"limit": 100}
)
Error Retry Strategy¶
Implement proper retry logic for transient errors:
import time
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_session():
"""Create a requests session with retry logic."""
session = requests.Session()
retry_strategy = Retry(
total=3,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["HEAD", "GET", "OPTIONS"],
backoff_factor=1
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("https://", adapter)
session.mount("http://", adapter)
return session
# Usage
session = create_session()
response = session.get("https://python.org/api/v1/releases")
Next Steps¶
Explore the API: Visit
/api/to use the interactive Scalar documentationRead Authentication Guide: See API Authentication for detailed auth flows
Check Rate Limits: Review the Rate Limiting Quick Reference
Generate a Client: Use the OpenAPI spec to generate a client SDK for your language
Report Issues: Found a bug? Report it at GitHub Issues
Support¶
For API-related questions and issues:
GitHub Issues: https://github.com/python/pythondotorg/issues
Security Issues: security@python.org
General Questions: https://discuss.python.org/
Last updated: 2025-11-29