Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,29 @@ Let's print the title of the first result, but in a more Pythonic way:

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.

### Error handling

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.

```python
import serpapi

client = serpapi.Client(api_key=os.getenv("API_KEY"))

try:
results = client.search({
'engine': 'google',
'q': 'coffee',
})
except serpapi.HTTPError as e:
if e.status_code == 401: # Invalid API key
print(e.error) # "Invalid API key. Your API key should be here: https://serpapi.com/manage-api-key"
elif e.status_code == 400: # Missing required parameter
pass
elif e.status_code == 429: # Exceeds the hourly throughput limit OR account run out of searches
pass
```

## Documentation

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


## License

MIT License.
Expand Down
16 changes: 15 additions & 1 deletion serpapi/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,21 @@ class SearchIDNotProvided(ValueError, SerpApiError):
class HTTPError(requests.exceptions.HTTPError, SerpApiError):
"""HTTP Error."""

pass
def __init__(self, original_exception):
if (isinstance(original_exception, requests.exceptions.HTTPError)):
http_error_exception: requests.exceptions.HTTPError = original_exception

self.status_code = http_error_exception.response.status_code
try:
self.error = http_error_exception.response.json().get("error", None)
except requests.exceptions.JSONDecodeError:
self.error = None
else:
self.status_code = -1
self.error = None

super().__init__(*original_exception.args, response=getattr(original_exception, 'response', None), request=getattr(original_exception, 'request', None))



class HTTPConnectionError(HTTPError, requests.exceptions.ConnectionError, SerpApiError):
Expand Down
17 changes: 17 additions & 0 deletions tests/test_exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from unittest.mock import Mock
import requests
import serpapi


def test_http_error():
"""Ensure that an HTTPError has the correct status code and error."""
mock_response = Mock()
mock_response.status_code = 401
mock_response.json.return_value = { "error": "Invalid API key" }

requests_error = requests.exceptions.HTTPError(response=mock_response, request=Mock())
http_error = serpapi.HTTPError(requests_error)

assert http_error.status_code == 401
assert http_error.error == "Invalid API key"
assert http_error.response == mock_response
12 changes: 11 additions & 1 deletion tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ def test_account_without_credentials():

def test_account_with_bad_credentials(invalid_key_client):
"""Ensure that an HTTPError is raised when account is accessed with invalid API Credentials."""
with pytest.raises(serpapi.HTTPError):
with pytest.raises(serpapi.HTTPError) as exc_info:
invalid_key_client.account()

assert exc_info.value.response.status_code == 401


def test_account_with_credentials(client):
Expand All @@ -38,6 +40,14 @@ def test_account_with_credentials(client):
assert isinstance(account, dict)


def test_search_with_missing_params(client):
with pytest.raises(serpapi.HTTPError) as exc_info:
client.search({ "q": "" })

assert exc_info.value.status_code == 400
assert "Missing query `q` parameter" in exc_info.value.error


def test_coffee_search(coffee_search):
assert isinstance(coffee_search, serpapi.SerpResults)
assert hasattr(coffee_search, "__getitem__")
Expand Down
Loading