Last modified: Oct 24, 2026
Mock APIs with responses Library
Testing code that talks to an API can be tricky. Real network calls are slow and unreliable. They can also fail for reasons outside your control.
The responses library solves this problem. It lets you mock HTTP responses in Python tests. Your code runs as if a real API answered, but nothing leaves your machine.
In this guide you will learn how to install and use responses. You will see clear examples with code and output. By the end you will be able to write fast and reliable API tests.
What Is the responses Library?
responses is a Python library for mocking the requests library. It intercepts outgoing HTTP calls and returns canned responses instead.
It was created by the team behind requests-mock alternatives and is widely used in the community. It works well with pytest and unittest.
With responses you can define a URL, a status code, and a body. When your code calls that URL, it gets your fake response back.
This keeps tests fast, deterministic, and offline-friendly. No API keys are needed. No rate limits apply.
Why Mock APIs at All?
Real APIs can be slow. They may go down or change without warning. They may also cost money per request.
Mocking removes these risks. Tests focus on your logic, not the network.
Mocking also lets you simulate edge cases. You can test a 500 error or a timeout on demand. That is hard to do with a live API.
If you already call services like OpenAI, you know how important stable tests are. The same idea applies when you work with OpenAI function calling in Python and want to avoid real API calls in tests.
Installing responses
Install the library with pip. It is small and has no heavy dependencies.
pip install responses
You also need the requests library, since responses patches it. Most projects already have it installed.
pip install requests
Check the installed version to confirm everything is ready.
pip show responses
Name: responses
Version: 0.25.3
Summary: A utility library for mocking out the `requests` Python library.
Your First Mocked Response
Start with a simple function that calls an API. Then mock that call in a test.
The @responses.activate decorator turns mocking on for the test. Inside the test you register a fake response with responses.add.
import requests
import responses
def get_user(user_id):
# Real code calls the API here
url = f"https://api.example.com/users/{user_id}"
return requests.get(url)
@responses.activate
def test_get_user():
# Register a fake response for the exact URL
responses.add(
responses.GET,
"https://api.example.com/users/1",
json={"id": 1, "name": "Alice"},
status=200,
)
response = get_user(1)
# Your code sees a normal requests response
print(response.status_code)
print(response.json())
test_get_user()
200
{'id': 1, 'name': 'Alice'}
Notice that get_user did not change at all. The mock lives only in the test. That is the key benefit of responses.
Checking That Calls Were Made
A mock is useful for more than returning data. It also records calls.
Use responses.calls to inspect what your code sent. You can assert on the URL, method, and headers.
import requests
import responses
@responses.activate
def test_call_count():
responses.add(
responses.POST,
"https://api.example.com/items",
json={"ok": True},
status=201,
)
requests.post("https://api.example.com/items", json={"name": "book"})
# Inspect recorded calls
assert len(responses.calls) == 1
call = responses.calls[0]
print(call.request.method)
print(call.request.url)
print(call.request.body)
test_call_count()
POST
https://api.example.com/items
{"name": "book"}
This pattern is great for verifying payloads. It also helps you catch accidental extra calls.
Mocking Errors and Status Codes
Real APIs fail sometimes. Good tests cover those cases.
Set the status argument to any HTTP code. You can also raise real exceptions to simulate network problems.
import requests
import responses
@responses.activate
def test_server_error():
responses.add(
responses.GET,
"https://api.example.com/data",
json={"error": "boom"},
status=500,
)
response = requests.get("https://api.example.com/data")
print(response.status_code)
print(response.json())
test_server_error()
500
{'error': 'boom'}
To simulate a timeout, pass a body that is an exception instance. The library will raise it when the call is made.
import requests
import responses
@responses.activate
def test_timeout():
responses.add(
responses.GET,
"https://api.example.com/slow",
body=requests.exceptions.Timeout("too slow"),
)
try:
requests.get("https://api.example.com/slow")
except requests.exceptions.Timeout as exc:
print("caught:", exc)
test_timeout()
caught: too slow
Now you can test retry logic and error handling without waiting for a real timeout.
Using responses with pytest
responses ships with a pytest fixture called responses. It is cleaner than the decorator in larger test suites.
Pass the fixture as an argument. Register mocks inside the test. Cleanup happens automatically.
import requests
def test_with_fixture(responses):
responses.add(
responses.GET,
"https://api.example.com/ping",
json={"pong": True},
status=200,
)
response = requests.get("https://api.example.com/ping")
assert response.json() == {"pong": True}
$ pytest test_api.py -v
test_api.py::test_with_fixture PASSED
The fixture is handy when you have many tests. It avoids repeating the decorator on every function.
Matching URLs with Regex and Callbacks
Sometimes the URL contains dynamic parts. Use a compiled regex instead of a fixed string.
You can also use responses.add_callback to return a response based on the request. This is useful for pagination or echo endpoints.
import re
import requests
import responses
@responses.activate
def test_regex_and_callback():
# Match any user id with a regex
responses.add(
responses.GET,
re.compile(r"https://api\.example\.com/users/\d+"),
json={"name": "Bob"},
status=200,
)
# Callback builds a response from the request
def echo(request):
return (200, {"headers": dict(request.headers)})
responses.add_callback(
responses.GET,
"https://api.example.com/echo",
callback=echo,
)
print(requests.get("https://api.example.com/users/42").json())
print(requests.get("https://api.example.com/echo").json())
test_regex_and_callback()
{'name': 'Bob'}
{'headers': {'User-Agent': 'python-requests/2.31.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}}
Regex matching keeps tests flexible. Callbacks give you full control over the fake response.
Common Pitfalls
You may see an error like ConnectionError when a mock is missing. That means the URL did not match any registered response.
Check the method, URL, and any query string. A trailing slash matters. So do query parameters, unless you use match_querystring or a regex.
Another pitfall is ordering. By default, responses are matched in the order they were registered. Use the match parameter to control this.
Also remember that responses patches requests only. Code using other HTTP clients, like httpx, will not be mocked.
When to Use It in a Real Project
Use responses in unit tests for any module that calls a REST API. It keeps tests isolated and fast.
It also helps when you test data pipelines. For example, a test that parses text from an API can avoid real calls and focus on parsing. The same principle applies when you write tests for tokenization with NLTK in Python and want deterministic input.
For message-based systems, mocking HTTP is only one piece. You may still need real integration tests for brokers. Tools like producing and consuming Kafka messages in Python are often tested separately from HTTP mocks.
Keep unit tests fast with mocks. Keep a small set of integration tests real. That balance gives you confidence without slow suites.
Conclusion
The responses library makes API mocking simple in Python. It patches requests and returns the responses you define.
You learned to install it, register mocks, inspect calls, and simulate errors. You also saw pytest fixtures, regex matching, and callbacks.
Start small. Mock one endpoint in one test. Then expand to cover errors and edge cases.
With responses, your tests become fast, reliable, and easy to read. That is a big win for any project that depends on external APIs.