Making external requests¶
This page covers guidelines and requirements for making HTTP requests to external APIs from your Lime CRM solutions and packages.
Always specify timeout for external HTTP requests¶
Breaking change in lime-crm 2.1135.0
Starting with lime-crm 2.1135.0 (released September 2025), using Python requests
library without the timeout
parameter will raise an error in pytest and emit a UserWarning
at runtime. This change ensures that HTTP requests don't hang indefinitely and helps maintain system stability.
When making HTTP requests to external APIs using the requests
library, you must always specify a timeout
parameter. This prevents requests from hanging indefinitely if the external service is slow or unresponsive.
Note
This requirement applies to all Python code in your solution or package, including event handlers, custom endpoints, scheduled tasks, and custom limeobjects.
Non-compliant code (will cause errors):
import requests
# This will raise an error in pytest and emit a warning at runtime
response = requests.get('https://api.example.com/data')
response = requests.post('https://api.example.com/create', json=data)
Compliant code (recommended):
import requests
# Always specify a timeout value (in seconds)
response = requests.get('https://api.example.com/data', timeout=30)
response = requests.post('https://api.example.com/create', json=data, timeout=30)
# For long-running requests, adjust the timeout accordingly
response = requests.get('https://api.example.com/large-file', timeout=120)
The timeout value should be chosen based on the expected response time of the external API. A typical value is 30 seconds, but adjust this based on your specific use case. For APIs that process large amounts of data or perform complex operations, you may need a higher timeout value.
Testing external requests¶
When testing code that makes external HTTP requests, use pytest-httpserver
to mock the external service. This allows you to test your code without making actual network calls and ensures tests are fast and reliable.
Testing retry logic¶
Here's an example of how to test that your code properly handles retries when the external service returns a 429 (Too Many Requests) error:
import pytest
import requests
from pytest_httpserver import HTTPServer
from urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter
def test_request_retries_gives_retry_error(httpserver: HTTPServer):
# Simulate a 429 (Too Many Requests) response
httpserver.expect_request("/api/data").respond_with_data("", status=429)
# Configure session with retry logic
session = requests.Session()
retry_strategy = Retry(
total=1,
backoff_factor=1,
status_forcelist=[429, 503, 504, 509],
respect_retry_after_header=True,
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
# Make the request - should raise RetryError after exhausting retries
with pytest.raises(requests.exceptions.RetryError):
session.get(httpserver.url_for("/api/data"), timeout=30)
Testing error handling¶
Here's an example of how to test that your code properly handles HTTP errors that should not be retried (like 401 Unauthorized):
def test_request_no_retries_gives_http_error(httpserver: HTTPServer):
# Simulate a 401 (Unauthorized) response
httpserver.expect_request("/api/data").respond_with_data("", status=401)
# Configure session with retry logic (401 is NOT in the retry list)
session = requests.Session()
retry_strategy = Retry(
total=1,
backoff_factor=1,
status_forcelist=[429, 503, 504, 509],
respect_retry_after_header=True,
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
# Make the request - should raise HTTPError immediately without retries
response = session.get(httpserver.url_for("/api/data"), timeout=30)
with pytest.raises(requests.HTTPError):
response.raise_for_status()