This guide helps you migrate your code from v4.3.1 to v5 of the Cloudflare Python SDK.
Version 5 introduces several breaking changes across multiple resources. This guide provides detailed migration instructions for each affected resource.
Important: This is a beta release. APIs and types may change before the final v5.0.0 release.
Resources with breaking changes:
- Abuse Reports
- ACM Total TLS
- API Gateway Configurations
- Cloudforce One Threat Events
- D1 Database
- Intel Indicator Feeds
- Logpush Edge
- Origin TLS Client Auth Hostnames
- Queues Consumers
- Radar BGP
- Rulesets Rules
- Schema Validation Schemas
- Snippets
- Zero Trust DLP
- Zero Trust Networks
Resource: abusereports
This resource has breaking changes.
1. create() endpoint changed from post /accounts/{account_id}/abuse-reports/{report_type} to post /accounts/{account_id}/abuse-reports/{report_param}
Actions Needed:
Review the changes above and update your code accordingly.
Resource: acm.totaltls
The ACM Total TLS resource has undergone a method restructuring in v5. The create() method has been removed and replaced with two new methods: update() and edit(), both of which perform similar functionality but with slightly different semantics.
What changed:
The create() method has been completely removed and replaced with update() and edit() methods. Both new methods use the same endpoint (POST /zones/{zone_id}/acm/total_tls) and similar parameters.
Before (v4.3.1):
client.acm.total_tls.create(
zone_id="023e105f4ecef8ad9ca31a8372d0c353",
enabled=True,
certificate_authority="google"
)After (v5):
# Option 1: Use update()
client.acm.total_tls.update(
zone_id="023e105f4ecef8ad9ca31a8372d0c353",
enabled=True,
certificate_authority="google"
)
# Option 2: Use edit()
client.acm.total_tls.edit(
zone_id="023e105f4ecef8ad9ca31a8372d0c353",
enabled=True,
certificate_authority="google"
)Actions Needed:
- Replace all calls to
create()with eitherupdate()oredit() - The parameters remain the same:
zone_id,enabled, and optionalcertificate_authority - Update import statements if needed:
- Remove:
from cloudflare.types.acm.total_tls_create_response import TotalTLSCreateResponse - Add:
from cloudflare.types.acm.total_tls_update_response import TotalTLSUpdateResponse - Or:
from cloudflare.types.acm.total_tls_edit_response import TotalTLSEditResponse
- Remove:
What changed:
The optional parameter certificate_authority now uses the Omit type instead of NotGiven, and its default value changed from NOT_GIVEN to omit.
Before (v4.3.1):
from cloudflare._types import NOT_GIVEN
result = client.acm.total_tls.create(
zone_id="023e105f4ecef8ad9ca31a8372d0c353",
enabled=True,
certificate_authority=NOT_GIVEN # Old sentinel value
)After (v5):
from cloudflare._types import omit
result = client.acm.total_tls.update(
zone_id="023e105f4ecef8ad9ca31a8372d0c353",
enabled=True,
certificate_authority=omit # New sentinel value
)Actions Needed:
- If you explicitly used
NOT_GIVEN, replace it withomit - Update imports from
NOT_GIVENtoomit - If you relied on the default behavior (not passing the parameter), no changes are needed
This is part of a broader SDK improvement to provide more precise type hints and better distinguish between omitted parameters and null values.
What changed:
The response type has changed from TotalTLSCreateResponse to either TotalTLSUpdateResponse or TotalTLSEditResponse.
Before (v4.3.1):
from cloudflare.types.acm.total_tls_create_response import TotalTLSCreateResponse
result: TotalTLSCreateResponse = client.acm.total_tls.create(
zone_id="023e105f4ecef8ad9ca31a8372d0c353",
enabled=True
)After (v5):
from cloudflare.types.acm.total_tls_update_response import TotalTLSUpdateResponse
result: TotalTLSUpdateResponse = client.acm.total_tls.update(
zone_id="023e105f4ecef8ad9ca31a8372d0c353",
enabled=True
)Actions Needed:
- Update type annotations in your code
- Update import statements to use the new response types
- The response structure remains the same, only the type name has changed
Response types were renamed to match the new method names for consistency across the SDK.
Resource: apigateway.configurations
This resource has breaking changes.
Actions Needed:
Review the changes above and update your code accordingly.
Resource: cloudforceone.threatevents
The Cloudforce One Threat Events resource has undergone significant changes in v5. The insights and crons sub-resources have been completely removed, and the get() method has been deprecated. Several method signatures have been updated to make previously required parameters optional and add support for new features like multiple indicators per event.
What changed:
The entire insights sub-resource has been removed, including all associated methods (create(), delete(), edit(), get()) and their response types.
Before (v4.3.1):
from cloudflare import Cloudflare
client = Cloudflare()
insights = client.cloudforce_one.threat_events.insights
result = insights.create(
account_id="abc123",
)After (v5):
# Use the 'insight' parameter when creating events:
result = client.cloudforce_one.threat_events.create(
path_account_id="abc123",
category="malware",
date="2024-01-01T00:00:00Z",
event="event description",
raw={"key": "value"},
tlp="green",
insight="your insight text here" # New parameter
)Actions Needed:
- Replace all
insightssub-resource calls with the main threat events API - Use the new
insightparameter when creating or editing events - Update import statements to remove insight-related types:
- Remove:
InsightCreateResponse,InsightDeleteResponse,InsightEditResponse,InsightGetResponse
- Remove:
- Refactor code that accessed
client.cloudforce_one.threat_events.insightsto use the main API
Insights functionality was consolidated into the main threat events resource to simplify the API structure and make it easier to attach insights directly to events.
What changed:
The entire crons sub-resource has been completely removed from the threat events API.
Before (v4.3.1):
from cloudflare import Cloudflare
client = Cloudflare()
crons = client.cloudforce_one.threat_events.cronsAfter (v5):
Actions Needed:
- Remove all references to
client.cloudforce_one.threat_events.crons - If you relied on cron functionality, contact Cloudflare support for alternative scheduling solutions
- Consider using external scheduling mechanisms if needed
The crons functionality was removed as part of the API restructuring. Scheduling operations should be handled outside the threat events API.
What changed:
Several parameters in the create() method changed from required to optional, and new parameters were added. The dataset_id parameter now has different default behavior.
Before (v4.3.1):
result = client.cloudforce_one.threat_events.create(
path_account_id="abc123",
attacker="attacker-name", # Required
attacker_country="US", # Required
category="malware",
date="2024-01-01T00:00:00Z",
event="event description",
indicator_type="ipv4", # Required
raw={"key": "value"},
tlp="green",
dataset_id="dataset-123" # Must be specified
)After (v5):
result = client.cloudforce_one.threat_events.create(
path_account_id="abc123",
category="malware",
date="2024-01-01T00:00:00Z",
event="event description",
raw={"key": "value"},
tlp="green",
# Optional parameters:
attacker="attacker-name", # Now optional, can be None
attacker_country="US", # Now optional
indicator_type="ipv4", # Now optional
dataset_id="dataset-123", # Optional - uses default if omitted
indicator="192.168.1.1", # Single indicator (legacy)
indicators=[ # New: Multiple indicators support
{"type": "ipv4", "value": "192.168.1.1"},
{"type": "domain", "value": "malicious.com"}
],
insight="threat intelligence insight" # New parameter
)Actions Needed:
- Update method calls -
attacker,attacker_country, andindicator_typeare now optional - If you want to omit the attacker, set it to
Noneexplicitly or use theomitsentinel - Consider using the new
indicatorsarray parameter for events with multiple indicators - If you were always specifying
dataset_id, continue doing so; if not specified, events will be created in the default "Cloudforce One Threat Events" dataset - Add
insightparameter when you want to attach insights to events
The changes make the API more flexible by allowing events to be created without always specifying an attacker, and enable more complex scenarios like events with multiple indicators. The default dataset behavior simplifies usage for common cases.
What changed:
The list() method now supports cursor-based pagination and STIX2 format export. The dataset_id parameter behavior changed.
Before (v4.3.1):
from typing import List
result = client.cloudforce_one.threat_events.list(
account_id="abc123",
dataset_id=["dataset-123"], # Required, as List[str]
page=1,
page_size=50
)After (v5):
from cloudflare._types import SequenceNotStr
result = client.cloudforce_one.threat_events.list(
account_id="abc123",
dataset_id=["dataset-123"], # Optional, now SequenceNotStr[str]
cursor="cursor-token", # New: cursor-based pagination
format="json", # New: "json" or "stix2"
page=1,
page_size=50
)Actions Needed:
- The
dataset_idparameter is now optional - omit it to list from the default dataset - Update type hints from
List[str]toSequenceNotStr[str]if you have explicit type annotations - For large result sets (beyond 100,000 records), use the new
cursorparameter for better performance:# First request result = client.cloudforce_one.threat_events.list( account_id="abc123", page_size=50 ) # Get next page using cursor next_cursor = result.result_info.cursor result = client.cloudforce_one.threat_events.list( account_id="abc123", cursor=next_cursor, page_size=50 )
- Use
format="stix2"if you need STIX2-formatted output
Cursor-based pagination provides better performance and supports deep pagination beyond the 100,000 record limit of offset-based pagination. STIX2 format support enables better integration with threat intelligence platforms.
What changed:
The get() method is now deprecated and marked for removal in a future version.
Before (v4.3.1):
result = client.cloudforce_one.threat_events.get(
event_id="event-123",
account_id="abc123"
)After (v5):
result = client.cloudforce_one.threat_events.get(
event_id="event-123",
account_id="abc123"
)
# Use the dataset-specific endpoint via the datasets sub-resource:
result = client.cloudforce_one.threat_events.datasets.events.get(
dataset_id="dataset-123",
event_id="event-123",
account_id="abc123"
)Actions Needed:
- Migrate away from the deprecated
get()method as soon as possible - Use the dataset-specific events endpoint:
/events/dataset/:dataset_id/events/:event_id - Access this via the datasets sub-resource API (refer to datasets documentation)
- Update your code to handle the new endpoint structure
The new API structure requires dataset context for retrieving individual events, providing better data organization and access control.
What changed:
The edit() method now supports additional optional parameters including created_at, dataset_id, insight, and raw.
Before (v4.3.1):
result = client.cloudforce_one.threat_events.edit(
event_id="event-123",
account_id="abc123",
attacker="new-attacker",
category="malware",
date="2024-01-01T00:00:00Z"
)After (v5):
result = client.cloudforce_one.threat_events.edit(
event_id="event-123",
account_id="abc123",
attacker="new-attacker", # Can now be None
category="malware",
date="2024-01-01T00:00:00Z",
# New optional parameters:
created_at="2024-01-01T00:00:00Z",
dataset_id="dataset-123",
insight="updated insight",
raw={"updated": "data"}
)Actions Needed:
- No changes required for existing code
- Optionally use new parameters to update additional fields:
- Use
created_atto set the creation timestamp - Use
dataset_idto move events between datasets - Use
insightto update or add insights - Use
rawto update raw event data
- Use
- The
attackerparameter can now be set toNoneto remove the attacker
Additional parameters provide more comprehensive event editing capabilities and better align with the new event model that includes insights and dataset references.
What changed:
The bulk_create() method now supports an optional include_created_events parameter.
Before (v4.3.1):
result = client.cloudforce_one.threat_events.bulk_create(
account_id="abc123",
data=[
{"category": "malware", "date": "2024-01-01T00:00:00Z", ...},
{"category": "phishing", "date": "2024-01-02T00:00:00Z", ...}
],
dataset_id="dataset-123"
)After (v5):
result = client.cloudforce_one.threat_events.bulk_create(
account_id="abc123",
data=[
{"category": "malware", "date": "2024-01-01T00:00:00Z", ...},
{"category": "phishing", "date": "2024-01-02T00:00:00Z", ...}
],
dataset_id="dataset-123",
include_created_events=True # New parameter
)Actions Needed:
- No changes required for existing code
- Set
include_created_events=Trueif you need to track which events were created and their locations:result = client.cloudforce_one.threat_events.bulk_create( account_id="abc123", data=events_data, dataset_id="dataset-123", include_created_events=True ) # Access created event details for event in result.created_events: print(f"Created event {event.uuid} on shard {event.shard_id}")
The new parameter provides better tracking and debugging capabilities for bulk operations, allowing developers to verify which events were successfully created and where they're stored.
Resource: d1.database
The D1 Database resource has been completely rewritten in v5. While the new implementation provides all the same core functionality, the API surface has changed significantly. All methods that existed in v4.3.1 have been removed and replaced with new implementations that have different signatures, particularly around path structure and method organization.
IMPORTANT: Despite methods having the same names (e.g., create(), delete(), etc.), the new implementation is not a drop-in replacement. The path parameter structure has changed, requiring code updates.
What changed:
The entire D1 Database resource was regenerated from the updated OpenAPI specification. While method names remain similar, the internal implementation and some signatures have changed.
Impact:
This affects all methods: create(), update(), list(), delete(), edit(), export(), get(), params(), query(), raw(), and import_().
What changed:
The endpoint path and parameter structure for creating databases has changed.
Before (v4.3.1):
from cloudflare import Cloudflare
client = Cloudflare()
result = client.d1.database.create(
account_id="abc123",
name="my-database",
)After (v5):
from cloudflare import Cloudflare
client = Cloudflare()
result = client.d1.database.create(
account_id="abc123",
name="my-database",
jurisdiction="eu", # Optional
primary_location_hint="weur" # Optional
)Actions Needed:
- Update the
create()method call to use the new parameter structure - Use the optional
jurisdictionparameter to restrict where the D1 database runs and stores data:- Values:
"eu"or"fedramp"
- Values:
- Use the optional
primary_location_hintparameter to specify the region:- Values:
"wnam","enam","weur","eeur","apac","oc"
- Values:
- Note: If
jurisdictionis specified, theprimary_location_hintis ignored
The new implementation provides explicit control over data residency and regional placement, which is important for compliance and performance requirements.
What changed:
The update() method now specifically handles read replication configuration.
Before (v4.3.1):
result = client.d1.database.update(
)After (v5):
result = client.d1.database.update(
database_id="def456",
account_id="abc123",
read_replication={
"enabled": True,
"regions": ["weur", "enam"]
}
)Actions Needed:
- The
update()method now requires adatabase_idas the first positional parameter - The method specifically handles read replication configuration
- For other database updates, use the new
edit()method (PATCH) instead - The
read_replicationparameter is required and must include:enabled: Boolean flagregions: Optional list of regions for read replicas
The separation of update() (PUT - full updates) and edit() (PATCH - partial updates) follows RESTful conventions more closely and provides clearer semantics for different types of updates.
What changed:
A new edit() method was added for partial updates using HTTP PATCH.
Before (v4.3.1):
After (v5):
result = client.d1.database.edit(
database_id="def456",
account_id="abc123",
read_replication={
"enabled": True
}
)Actions Needed:
- Use
edit()for partial updates where you want to modify specific fields - Use
update()when you want to replace the entire configuration - Both methods currently support
read_replicationconfiguration - The
database_idis required as the first positional parameter
Having both update() and edit() methods provides flexibility for different update scenarios and follows standard REST API patterns.
What changed:
The list() method now returns a paginated response type instead of a simple list.
Before (v4.3.1):
result = client.d1.database.list(
account_id="abc123"
)After (v5):
from cloudflare.pagination import SyncV4PagePaginationArray
result = client.d1.database.list(
account_id="abc123",
name="search-term", # Optional: filter by name
page=1, # Optional: page number
per_page=20 # Optional: items per page
)
for database in result:
print(database.name)Actions Needed:
- Update code that expects a simple list to handle pagination
- Use the new optional parameters for filtering and pagination:
name: Search for databases by namepage: Specify page number (1-indexed)per_page: Set number of items per page
- Iterate through the paginated results or convert to a list if needed:
all_databases = list(result)
Pagination support allows efficient handling of large numbers of databases and follows the standard pattern used across the Cloudflare API.
What changed:
The delete() method now returns a generic object type instead of a specific response type.
Before (v4.3.1):
result = client.d1.database.delete(
database_id="def456",
account_id="abc123"
)After (v5):
result = client.d1.database.delete(
database_id="def456",
account_id="abc123"
)Actions Needed:
- Update type annotations to expect
objectinstead of a specific response type - The method signature requires
database_idas the first positional parameter - The operation still returns a confirmation, but without a specific type structure
The generic return type simplifies the API and aligns with the actual API behavior.
What changed:
The export() method now has a clearer parameter structure with required and optional fields.
Before (v4.3.1):
result = client.d1.database.export(
)After (v5):
result = client.d1.database.export(
database_id="def456",
account_id="abc123",
output_format="polling", # Required: must be "polling"
current_bookmark="bookmark", # Optional: for polling in-progress export
dump_options={ # Optional: export configuration
"no_data": False,
"no_schema": False,
"tables": ["table1", "table2"]
}
)
while result.status == "in_progress":
result = client.d1.database.export(
database_id="def456",
account_id="abc123",
output_format="polling",
current_bookmark=result.bookmark
)
if result.status == "complete":
download_url = result.urlActions Needed:
- Always specify
output_format="polling"(currently the only supported format) - Use the
current_bookmarkparameter to poll for export completion - Configure export options using
dump_options:no_data: Set toTrueto export schema onlyno_schema: Set toTrueto export data onlytables: List specific tables to export
- Implement polling logic to wait for export completion
- Note: Exports block the database, so continue polling to avoid automatic cancellation
The new structure makes the async export process more explicit and provides better control over what gets exported.
What changed:
The import_() method now uses overloaded signatures for different import actions.
Before (v4.3.1):
result = client.d1.database.import_(
)After (v5):
result = client.d1.database.import_(
database_id="def456",
account_id="abc123",
action="init",
etag="md5-hash-of-file"
)
upload_url = result.upload_url
result = client.d1.database.import_(
database_id="def456",
account_id="abc123",
action="ingest",
etag="md5-hash-of-file",
filename="uploaded-filename"
)
result = client.d1.database.import_(
database_id="def456",
account_id="abc123",
action="poll",
current_bookmark="bookmark-from-ingest"
)Actions Needed:
-
Break your import logic into three stages:
Stage 1: Initialize
import hashlib # Calculate MD5 hash with open("database.sql", "rb") as f: etag = hashlib.md5(f.read()).hexdigest() # Get upload URL result = client.d1.database.import_( database_id="def456", account_id="abc123", action="init", etag=etag )
Stage 2: Upload and Ingest
# Upload file to the URL (use requests or similar) import requests with open("database.sql", "rb") as f: requests.put(result.upload_url, data=f) # Trigger import result = client.d1.database.import_( database_id="def456", account_id="abc123", action="ingest", etag=etag, filename="database.sql" ) bookmark = result.bookmark
Stage 3: Poll for Completion
while True: status = client.d1.database.import_( database_id="def456", account_id="abc123", action="poll", current_bookmark=bookmark ) if status.status == "complete": break time.sleep(5)
-
Always calculate and provide the MD5 etag of your SQL file
-
Use the
current_bookmarkto track import progress -
Note: Imports block the database for their duration
The overloaded signatures make each import stage explicit and type-safe, reducing errors and making the multi-step process clearer.
What changed:
The query() method now supports both single-query and batch-query modes via overloads.
Before (v4.3.1):
result = client.d1.database.query(
)After (v5):
from cloudflare.pagination import SyncSinglePage
result = client.d1.database.query(
database_id="def456",
account_id="abc123",
sql="SELECT * FROM users WHERE id = ?",
params=["123"] # Optional: parameterized query
)
result = client.d1.database.query(
database_id="def456",
account_id="abc123",
batch=[
{"sql": "SELECT * FROM users", "params": []},
{"sql": "SELECT * FROM posts", "params": []}
]
)
for query_result in result:
for row in query_result.results:
print(row)Actions Needed:
-
For single queries, use the
sqlparameter with optionalparams:result = client.d1.database.query( database_id="def456", account_id="abc123", sql="SELECT * FROM users WHERE active = ?", params=["true"] )
-
For batch queries, use the
batchparameter:result = client.d1.database.query( database_id="def456", account_id="abc123", batch=[ {"sql": "BEGIN TRANSACTION"}, {"sql": "INSERT INTO users (name) VALUES (?)", "params": ["John"]}, {"sql": "COMMIT"} ] )
-
The return type is now
SyncSinglePage[QueryResult]for pagination support -
Use parameterized queries (the
paramsarray) to prevent SQL injection -
Multiple statements in a single SQL string (separated by semicolons) are also supported
Overloads provide type safety for different query modes, and the batch mode enables efficient execution of multiple queries with transaction support.
What changed:
The raw() method now supports both single-query and batch-query modes, similar to query(), but returns results as arrays instead of objects for better performance.
Before (v4.3.1):
result = client.d1.database.raw(
)After (v5):
from cloudflare.pagination import SyncSinglePage
result = client.d1.database.raw(
database_id="def456",
account_id="abc123",
sql="SELECT id, name, email FROM users",
params=[]
)
for query_result in result:
for row in query_result.results:
# row = [1, "John", "john@example.com"]
user_id, name, email = row
print(f"{user_id}: {name} <{email}>")
result = client.d1.database.raw(
database_id="def456",
account_id="abc123",
batch=[
{"sql": "SELECT * FROM users"},
{"sql": "SELECT * FROM posts"}
]
)Actions Needed:
-
Use
raw()instead ofquery()when you need better performance -
Adapt your code to handle array results instead of object results:
Before (with query()):
result = client.d1.database.query( database_id="def456", account_id="abc123", sql="SELECT id, name FROM users" ) for row in result[0].results: print(row.id, row.name) # Object access
After (with raw()):
result = client.d1.database.raw( database_id="def456", account_id="abc123", sql="SELECT id, name FROM users" ) for row in result[0].results: print(row[0], row[1]) # Array access # Or use tuple unpacking: user_id, name = row print(user_id, name)
-
Use the
columnsfield to map array indices to column names if needed:query_result = list(result)[0] columns = {name: idx for idx, name in enumerate(query_result.columns)} for row in query_result.results: user_id = row[columns["id"]] name = row[columns["name"]]
-
Batch queries work the same way as in
query()
The raw() method provides a performance-optimized alternative to query() by returning results as arrays instead of objects, reducing memory overhead and serialization time for large result sets.
What changed:
The params() method has been completely removed with no direct replacement.
Before (v4.3.1):
result = client.d1.database.params(
)After (v5):
# Use query() or raw() methods with the params parameter instead
result = client.d1.database.query(
database_id="def456",
account_id="abc123",
sql="SELECT * FROM users WHERE id = ?",
params=["123"] # Pass params here
)Actions Needed:
- Remove all calls to
params() - Use parameterized queries in
query()orraw()methods:# Instead of a separate params() call, pass params inline: result = client.d1.database.query( database_id="def456", account_id="abc123", sql="SELECT * FROM users WHERE active = ? AND role = ?", params=["true", "admin"] )
The functionality was consolidated into the query methods themselves, making parameterized queries more intuitive and eliminating an unnecessary separate method.
What changed:
Several response types were removed and replaced with new ones.
Removed Types:
DatabaseImportResponseDatabaseExportResponseDatabaseListResponseDatabaseRawResponseQueryResult
Before (v4.3.1):
from cloudflare.types.d1 import (
DatabaseImportResponse,
DatabaseExportResponse,
DatabaseListResponse,
DatabaseRawResponse,
QueryResult
)After (v5):
from cloudflare.types.d1.database_import_response import DatabaseImportResponse
from cloudflare.types.d1.database_export_response import DatabaseExportResponse
from cloudflare.types.d1.database_list_response import DatabaseListResponse
from cloudflare.types.d1.database_raw_response import DatabaseRawResponse
from cloudflare.types.d1.query_result import QueryResultActions Needed:
- Update import statements to use the new module paths if you import types directly
- The types themselves may have internal structural changes - review your code that accesses response fields
- Most code should work without changes if you don't rely on specific type annotations
Types were regenerated from the updated OpenAPI spec to ensure accuracy and maintain consistency with the API.
What changed:
All methods now require explicit database_id where appropriate, moving from a potentially different parameter structure.
Before (v4.3.1):
result = client.d1.database.get(
account_id="abc123",
database_id="def456"
)After (v5):
result = client.d1.database.get(
database_id="def456", # First positional parameter
account_id="abc123" # Named parameter
)Actions Needed:
-
Review all D1 database method calls
-
Ensure
database_idis passed as the first parameter (afterself) for methods that operate on specific databases -
Methods that operate on a specific database:
update(database_id, ...)delete(database_id, ...)edit(database_id, ...)export(database_id, ...)get(database_id, ...)import_(database_id, ...)query(database_id, ...)raw(database_id, ...)
-
Methods that operate at the account level:
create(account_id, ...)- Creates a new databaselist(account_id, ...)- Lists all databases
Consistent parameter ordering improves API usability and follows RESTful conventions where resource identifiers appear earlier in the method signature.
Given the extensive nature of changes to the D1 Database resource, we recommend the following migration approach:
-
Audit Your Code: Identify all locations where you use
client.d1.database.*methods -
Update Method Calls: Go through each method call and update according to the specific breaking changes documented above
-
Test Thoroughly: The reimplementation means you should test all D1-related functionality, even if the method names haven't changed
-
Update Type Annotations: If you use explicit type hints, update them to match the new response types
-
Handle Pagination: Update code to handle paginated responses from
list()and query methods -
Review Error Handling: Error responses may have changed; update your error handling code accordingly
-
Consider Using Raw Queries: If performance is critical, evaluate switching from
query()toraw()for read operations
The D1 Database resource has been completely overhauled in v5. While this provides a more robust and feature-rich API, it requires careful migration. The key changes are:
- All methods have new implementations with updated signatures
- Path parameters are now consistently ordered
- Query methods support both single and batch modes
- Pagination support for list operations
- Better type safety with overloaded method signatures
- Removed
params()method in favor of inline parameterization - New
edit()method for partial updates - Enhanced import/export workflows with explicit multi-step processes
Plan for significant testing time when migrating D1 Database code to v5.
Resource: intel.indicatorfeeds
The Intel Indicator Feeds resource has undergone a structural change in v5 with the removal of the downloads sub-resource. The get() method that was previously available under the downloads sub-resource has been removed. The main indicator feeds functionality remains largely intact with minor parameter handling improvements.
What changed:
The entire downloads sub-resource has been completely removed, along with its get() method.
Before (v4.3.1):
from cloudflare import Cloudflare
client = Cloudflare()
downloads = client.intel.indicator_feeds.downloads
result = downloads.get(
feed_id=123,
account_id="abc123"
)After (v5):
# Use the main data() method instead:
data = client.intel.indicator_feeds.data(
feed_id=123,
account_id="abc123"
)Actions Needed:
-
Replace all references to
client.intel.indicator_feeds.downloadswith the main resource -
Replace
downloads.get()calls withindicator_feeds.data():Before:
downloads = client.intel.indicator_feeds.downloads result = downloads.get( feed_id=123, account_id="abc123" )
After:
result = client.intel.indicator_feeds.data( feed_id=123, account_id="abc123" )
-
Remove any imports related to the downloads sub-resource:
# Remove these imports from cloudflare.resources.intel.indicator_feeds.downloads import ( DownloadsResource, AsyncDownloadsResource ) from cloudflare.types.intel.indicator_feeds.download_get_response import ( DownloadGetResponse )
-
Update type annotations if you were using
DownloadGetResponse:# The data() method returns str result: str = client.intel.indicator_feeds.data( feed_id=123, account_id="abc123" )
The downloads functionality was consolidated into the main indicator feeds resource through the data() method, simplifying the API structure and eliminating redundant resource nesting.
What changed:
Optional parameters now use the Omit type instead of NotGiven, and default values changed from NOT_GIVEN to omit.
Before (v4.3.1):
from cloudflare._types import NOT_GIVEN, NotGiven
result = client.intel.indicator_feeds.create(
account_id="abc123",
name="My Feed",
description=NOT_GIVEN # Old sentinel value
)After (v5):
from cloudflare._types import omit, Omit
result = client.intel.indicator_feeds.create(
account_id="abc123",
name="My Feed",
description=omit # New sentinel value
)Actions Needed:
-
If you explicitly used
NOT_GIVEN, replace it withomit:# Before from cloudflare._types import NOT_GIVEN result = client.intel.indicator_feeds.create( account_id="abc123", name="My Feed", description=NOT_GIVEN ) # After from cloudflare._types import omit result = client.intel.indicator_feeds.create( account_id="abc123", name="My Feed", description=omit )
-
Update type hints from
NotGiventoOmit:# Before from cloudflare._types import NotGiven def create_feed(name: str, description: str | NotGiven = NOT_GIVEN): pass # After from cloudflare._types import Omit, omit def create_feed(name: str, description: str | Omit = omit): pass
-
If you relied on the default behavior (not passing optional parameters), no changes are needed:
# This works in both versions result = client.intel.indicator_feeds.create( account_id="abc123", name="My Feed" )
The Omit type provides more precise type hints and better distinguishes between omitted parameters and explicit null values, improving type safety across the SDK.
What changed:
The timeout parameter default value changed from NOT_GIVEN to not_given (note the lowercase).
Before (v4.3.1):
from cloudflare._types import NOT_GIVEN
result = client.intel.indicator_feeds.list(
account_id="abc123",
timeout=NOT_GIVEN
)After (v5):
from cloudflare._types import not_given
result = client.intel.indicator_feeds.list(
account_id="abc123",
timeout=not_given
)Actions Needed:
-
Replace
NOT_GIVENwithnot_givenfor timeout parameters:from cloudflare._types import not_given # Explicitly set timeout result = client.intel.indicator_feeds.list( account_id="abc123", timeout=30.0 # 30 seconds ) # Or use default result = client.intel.indicator_feeds.list( account_id="abc123" # timeout defaults to not_given )
-
In practice, you rarely need to explicitly pass the sentinel value - just omit the parameter:
# Preferred approach result = client.intel.indicator_feeds.list( account_id="abc123" )
The lowercase not_given is used consistently for timeout parameters across the SDK, while omit is used for business logic parameters, providing clearer semantic meaning.
What changed:
Several methods had their signatures updated to use the new Omit type, but the actual behavior remains the same.
Methods affected:
create()update()list()data()get()
Example - create() method:
# v4.3.1 signature:
def create(
self,
*,
account_id: str,
description: str | NotGiven = NOT_GIVEN,
name: str | NotGiven = NOT_GIVEN,
...
) -> Optional[IndicatorFeedCreateResponse]:
# v5 signature:
def create(
self,
*,
account_id: str,
description: str | Omit = omit,
name: str | Omit = omit,
...
) -> Optional[IndicatorFeedCreateResponse]:Before (v4.3.1):
from cloudflare import Cloudflare
from cloudflare._types import NOT_GIVEN
client = Cloudflare()
account_id = "abc123"
feed = client.intel.indicator_feeds.create(
account_id=account_id,
name="My Threat Feed",
description="Threat intelligence feed"
)
downloads = client.intel.indicator_feeds.downloads
data = downloads.get(
feed_id=feed.id,
account_id=account_id
)
updated = client.intel.indicator_feeds.update(
feed_id=feed.id,
account_id=account_id,
name="Updated Feed Name",
description=NOT_GIVEN
)
feeds = client.intel.indicator_feeds.list(
account_id=account_id
)After (v5):
from cloudflare import Cloudflare
from cloudflare._types import omit
client = Cloudflare()
account_id = "abc123"
feed = client.intel.indicator_feeds.create(
account_id=account_id,
name="My Threat Feed",
description="Threat intelligence feed"
)
# Get feed data (no longer uses downloads sub-resource)
data = client.intel.indicator_feeds.data(
feed_id=feed.id,
account_id=account_id
)
updated = client.intel.indicator_feeds.update(
feed_id=feed.id,
account_id=account_id,
name="Updated Feed Name",
description=omit # Or simply omit the parameter
)
# Or better: just omit optional parameters
updated = client.intel.indicator_feeds.update(
feed_id=feed.id,
account_id=account_id,
name="Updated Feed Name"
)
feeds = client.intel.indicator_feeds.list(
account_id=account_id
)
# Use new permissions sub-resource
permissions = client.intel.indicator_feeds.permissions.list(
account_id=account_id
)Actions Needed:
These changes are largely transparent if you're using the methods normally:
# This code works in both versions
result = client.intel.indicator_feeds.create(
account_id="abc123",
name="My Indicator Feed",
description="Feed description"
)
# Optional parameters can be omitted
result = client.intel.indicator_feeds.create(
account_id="abc123",
name="My Indicator Feed"
)If you have explicit type annotations:
# Before
from cloudflare._types import NotGiven, NOT_GIVEN
def create_feed(
name: str,
description: str | NotGiven = NOT_GIVEN
) -> None:
pass
# After
from cloudflare._types import Omit, omit
def create_feed(
name: str,
description: str | Omit = omit
) -> None:
passConsistent use of Omit across all methods improves type safety and provides better IDE support.
While not breaking changes, the indicator feeds resource gained new functionality through the permissions sub-resource:
Resource: logpush.edge
This resource has breaking changes.
1. get() endpoint changed from get /zones/{zone_id}/logpush/edge to get /zones/{zone_id}/logpush/edge/jobs
Actions Needed:
Review the changes above and update your code accordingly.
Resource: origintlsclientauth.hostnames
This resource has breaking changes.
Actions Needed:
Review the changes above and update your code accordingly.
Actions Needed:
Review the changes above and update your code accordingly.
Actions Needed:
Review the changes above and update your code accordingly.
4. get() endpoint changed from get /zones/{zone_id}/origin_tls_client_auth/hostnames/certificates/{certificate_id} to get /zones/{zone_id}/origin_tls_client_auth/hostnames/{hostname}
Actions Needed:
Review the changes above and update your code accordingly.
Actions Needed:
Review the changes above and update your code accordingly.
Actions Needed:
Review the changes above and update your code accordingly.
Actions Needed:
Review the changes above and update your code accordingly.
Actions Needed:
Review the changes above and update your code accordingly.
Actions Needed:
Review the changes above and update your code accordingly.
Resource: queues.consumers
The Queues Consumers resource has undergone a significant restructuring in v5. The primary breaking change is the modification of the get() method endpoint and the addition of a new list() method. This change better aligns with RESTful conventions by separating the concerns of listing all consumers from fetching a specific consumer.
What changed:
The get() method endpoint changed from listing all consumers to fetching a specific consumer by ID. A new list() method was added to replace the old get() behavior.
Before (v4.3.1):
from cloudflare import Cloudflare
client = Cloudflare()
consumers = client.queues.consumers.get(
queue_id="abc123",
account_id="def456"
)
for consumer in consumers:
print(f"Consumer: {consumer.consumer_id}")After (v5):
from cloudflare import Cloudflare
client = Cloudflare()
consumer = client.queues.consumers.get(
consumer_id="consumer-123", # New required parameter
account_id="def456",
queue_id="abc123"
)
print(f"Consumer: {consumer.consumer_id}")Actions Needed:
-
If you were listing all consumers, replace
get()withlist():# Before consumers = client.queues.consumers.get( queue_id="abc123", account_id="def456" ) # After consumers = client.queues.consumers.list( queue_id="abc123", account_id="def456" ) # Both return an iterable of consumers for consumer in consumers: print(consumer.consumer_id)
-
If you were getting a specific consumer (by filtering the list), use the new
get()directly:# Before (inefficient) consumers = client.queues.consumers.get( queue_id="abc123", account_id="def456" ) consumer = next(c for c in consumers if c.consumer_id == "consumer-123") # After (efficient) consumer = client.queues.consumers.get( consumer_id="consumer-123", account_id="def456", queue_id="abc123" )
-
Update type annotations:
# Before from cloudflare.pagination import SyncSinglePage from cloudflare.types.queues import Consumer consumers: SyncSinglePage[Consumer] = client.queues.consumers.get(...) # After - for list() from cloudflare.pagination import SyncSinglePage from cloudflare.types.queues import Consumer consumers: SyncSinglePage[Consumer] = client.queues.consumers.list(...) # After - for get() from cloudflare.types.queues import Consumer from typing import Optional consumer: Optional[Consumer] = client.queues.consumers.get(...)
The change aligns with RESTful API conventions where:
GET /resourcereturns a list of resourcesGET /resource/{id}returns a specific resource by ID
The previous implementation had get() returning a list, which was semantically incorrect. Now:
list()returns all consumers for a queueget()returns a specific consumer by ID
What changed:
A new list() method was added to retrieve all consumers for a queue.
Before (v4.3.1):
consumers = client.queues.consumers.get(
queue_id="abc123",
account_id="def456"
)After (v5):
# Use list() to list consumers
consumers = client.queues.consumers.list(
queue_id="abc123",
account_id="def456"
)Actions Needed:
Simply replace get() calls with list() when you need all consumers:
from cloudflare import Cloudflare
client = Cloudflare()
# List all consumers for a queue
consumers = client.queues.consumers.list(
queue_id="abc123",
account_id="def456"
)
# Iterate through consumers
for consumer in consumers:
print(f"Consumer ID: {consumer.consumer_id}")
print(f"Script: {consumer.script_name}")
print(f"Type: {consumer.type}")The list() method name clearly indicates its purpose of listing multiple resources, improving API discoverability and consistency with other Cloudflare resources.
What changed:
Optional parameters now use the Omit type instead of NotGiven, and default values changed from NOT_GIVEN to omit.
Before (v4.3.1):
from cloudflare._types import NOT_GIVEN, NotGiven
result = client.queues.consumers.create(
queue_id="abc123",
account_id="def456",
script_name="my-worker",
dead_letter_queue=NOT_GIVEN,
settings=NOT_GIVEN
)After (v5):
from cloudflare._types import omit, Omit
result = client.queues.consumers.create(
queue_id="abc123",
account_id="def456",
script_name="my-worker",
dead_letter_queue=omit,
settings=omit
)Actions Needed:
-
Replace imports:
# Before from cloudflare._types import NOT_GIVEN, NotGiven # After from cloudflare._types import omit, Omit
-
Replace sentinel values:
# Before result = client.queues.consumers.create( queue_id="abc123", account_id="def456", dead_letter_queue=NOT_GIVEN ) # After result = client.queues.consumers.create( queue_id="abc123", account_id="def456", dead_letter_queue=omit )
-
Update type hints:
# Before from cloudflare._types import NotGiven def create_consumer( script_name: str, settings: dict | NotGiven = NOT_GIVEN ) -> None: pass # After from cloudflare._types import Omit, omit def create_consumer( script_name: str, settings: dict | Omit = omit ) -> None: pass
-
Preferred approach - omit optional parameters entirely:
# Best practice: just don't pass optional parameters result = client.queues.consumers.create( queue_id="abc123", account_id="def456", script_name="my-worker" # No need to explicitly pass omit for optional parameters )
The Omit type provides better type safety and clearer semantics for distinguishing between omitted parameters and explicit null values.
What changed:
The timeout parameter default value changed from NOT_GIVEN to not_given (lowercase).
Before (v4.3.1):
from cloudflare._types import NOT_GIVEN
result = client.queues.consumers.list(
queue_id="abc123",
account_id="def456",
timeout=NOT_GIVEN
)After (v5):
from cloudflare._types import not_given
result = client.queues.consumers.list(
queue_id="abc123",
account_id="def456",
timeout=not_given
)Actions Needed:
In practice, you rarely need to explicitly pass the sentinel value:
# Specify a custom timeout
result = client.queues.consumers.list(
queue_id="abc123",
account_id="def456",
timeout=30.0 # 30 seconds
)
# Or use default (preferred)
result = client.queues.consumers.list(
queue_id="abc123",
account_id="def456"
# timeout defaults to not_given
)Consistent use of lowercase not_given for infrastructure parameters like timeout improves consistency across the SDK.
The following methods retain the same functionality with updated type signatures:
Resource: radar.bgp
This resource has breaking changes.
1. timeseries() endpoint changed from get /radar/bgp/ips/timeseries to get /radar/bgp/rpki/aspa/timeseries
Actions Needed:
Review the changes above and update your code accordingly.
Actions Needed:
Review the changes above and update your code accordingly.
Resource: rulesets.rules
This resource has breaking changes.
Actions Needed:
Review the changes above and update your code accordingly.
Resource: schemavalidation.schemas
This resource has breaking changes.
Actions Needed:
Review the changes above and update your code accordingly.
Actions Needed:
Review the changes above and update your code accordingly.
Actions Needed:
Review the changes above and update your code accordingly.
Actions Needed:
Review the changes above and update your code accordingly.
Resource: snippets
This resource has breaking changes.
Actions Needed:
Review the changes above and update your code accordingly.
Resource: zerotrust.dlp
This resource has breaking changes.
1. update() endpoint changed from put /accounts/{account_id}/dlp/entries/{entry_id} to put /accounts/{account_id}/dlp/entries/integration/{entry_id}
Actions Needed:
Review the changes above and update your code accordingly.
Actions Needed:
Review the changes above and update your code accordingly.
Actions Needed:
Review the changes above and update your code accordingly.
Actions Needed:
Review the changes above and update your code accordingly.
Actions Needed:
Review the changes above and update your code accordingly.
Resource: zerotrust.networks
This resource has breaking changes.
Actions Needed:
Review the changes above and update your code accordingly.
Actions Needed:
Review the changes above and update your code accordingly.