Skip to content

Commit 01081fc

Browse files
authored
serpapi.HTTPError missing the essential status code and error (#22)
* Populate HTTPError with status code and error * Add error handling example to README * Handle edge cases in HTTPError
1 parent 7ae5887 commit 01081fc

File tree

4 files changed

+66
-3
lines changed

4 files changed

+66
-3
lines changed

README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,29 @@ Let's print the title of the first result, but in a more Pythonic way:
5050

5151
The [SerpApi.com API Documentation](https://serpapi.com/search-api) contains a list of all the possible parameters that can be passed to the API.
5252

53+
### Error handling
54+
55+
Unsuccessful requests raise `serpapi.HTTPError` exception. The returned status code will reflect the sort of error that occurred, please refer to [Status and Error Codes Documentation](https://serpapi.com/api-status-and-error-codes) for more details.
56+
57+
```python
58+
import serpapi
59+
60+
client = serpapi.Client(api_key=os.getenv("API_KEY"))
61+
62+
try:
63+
results = client.search({
64+
'engine': 'google',
65+
'q': 'coffee',
66+
})
67+
except serpapi.HTTPError as e:
68+
if e.status_code == 401: # Invalid API key
69+
print(e.error) # "Invalid API key. Your API key should be here: https://serpapi.com/manage-api-key"
70+
elif e.status_code == 400: # Missing required parameter
71+
pass
72+
elif e.status_code == 429: # Exceeds the hourly throughput limit OR account run out of searches
73+
pass
74+
```
75+
5376
## Documentation
5477

5578
Documentation is [available on Read the Docs](https://serpapi-python.readthedocs.io/en/latest/).
@@ -339,7 +362,6 @@ results = client.search({
339362
```
340363
- API Documentation: [serpapi.com/images-results](https://serpapi.com/images-results)
341364

342-
343365
## License
344366

345367
MIT License.

serpapi/exceptions.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,21 @@ class SearchIDNotProvided(ValueError, SerpApiError):
2222
class HTTPError(requests.exceptions.HTTPError, SerpApiError):
2323
"""HTTP Error."""
2424

25-
pass
25+
def __init__(self, original_exception):
26+
if (isinstance(original_exception, requests.exceptions.HTTPError)):
27+
http_error_exception: requests.exceptions.HTTPError = original_exception
28+
29+
self.status_code = http_error_exception.response.status_code
30+
try:
31+
self.error = http_error_exception.response.json().get("error", None)
32+
except requests.exceptions.JSONDecodeError:
33+
self.error = None
34+
else:
35+
self.status_code = -1
36+
self.error = None
37+
38+
super().__init__(*original_exception.args, response=getattr(original_exception, 'response', None), request=getattr(original_exception, 'request', None))
39+
2640

2741

2842
class HTTPConnectionError(HTTPError, requests.exceptions.ConnectionError, SerpApiError):

tests/test_exceptions.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from unittest.mock import Mock
2+
import requests
3+
import serpapi
4+
5+
6+
def test_http_error():
7+
"""Ensure that an HTTPError has the correct status code and error."""
8+
mock_response = Mock()
9+
mock_response.status_code = 401
10+
mock_response.json.return_value = { "error": "Invalid API key" }
11+
12+
requests_error = requests.exceptions.HTTPError(response=mock_response, request=Mock())
13+
http_error = serpapi.HTTPError(requests_error)
14+
15+
assert http_error.status_code == 401
16+
assert http_error.error == "Invalid API key"
17+
assert http_error.response == mock_response

tests/test_integration.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ def test_account_without_credentials():
2626

2727
def test_account_with_bad_credentials(invalid_key_client):
2828
"""Ensure that an HTTPError is raised when account is accessed with invalid API Credentials."""
29-
with pytest.raises(serpapi.HTTPError):
29+
with pytest.raises(serpapi.HTTPError) as exc_info:
3030
invalid_key_client.account()
31+
32+
assert exc_info.value.response.status_code == 401
3133

3234

3335
def test_account_with_credentials(client):
@@ -38,6 +40,14 @@ def test_account_with_credentials(client):
3840
assert isinstance(account, dict)
3941

4042

43+
def test_search_with_missing_params(client):
44+
with pytest.raises(serpapi.HTTPError) as exc_info:
45+
client.search({ "q": "" })
46+
47+
assert exc_info.value.status_code == 400
48+
assert "Missing query `q` parameter" in exc_info.value.error
49+
50+
4151
def test_coffee_search(coffee_search):
4252
assert isinstance(coffee_search, serpapi.SerpResults)
4353
assert hasattr(coffee_search, "__getitem__")

0 commit comments

Comments
 (0)