Skip to content

Commit 24637ba

Browse files
authored
Add request timeout (#24)
* feat: Introduce request timeout handling and allow passing requests-specific arguments to API calls, along with updating documentation and removing the README template. * Set version to 0.1.6 and add support for request timeouts * update changelog history * fix broken link
1 parent 01081fc commit 24637ba

File tree

8 files changed

+101
-173
lines changed

8 files changed

+101
-173
lines changed

HISTORY.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
Release History
22
===============
33

4+
0.1.6 (2026-02-16)
5+
------------------
6+
7+
- Add support for request timeouts.
8+
- Add status and error codes support - https://serpapi.com/api-status-and-error-codes
9+
410
0.1.5 (2023-11-01)
511
------------------
612

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<h1 align="center">SerpApi Python Library & Package</h1>
44
<img src="https://user-images.githubusercontent.com/78694043/233921372-bb57c347-9005-4b59-8f09-993698a87eb6.svg" width="600" alt="serpapi python library logo">
55

6-
<a href="https://badge.fury.io/py/serpapi-python">![Package](https://badge.fury.io/py/serpapi.svg)</a>
6+
<a href="https://pypi.org/project/serpapi">![Package](https://badge.fury.io/py/serpapi.svg)</a>
77

88
[![serpapi-python](https://github.com/serpapi/serpapi-python/actions/workflows/ci.yml/badge.svg)](https://github.com/serpapi/serpapi-python/actions/workflows/ci.yml)
99
</div>
@@ -52,12 +52,13 @@ The [SerpApi.com API Documentation](https://serpapi.com/search-api) contains a l
5252

5353
### Error handling
5454

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.
55+
Unsuccessful requests raise `serpapi.HTTPError` or `serpapi.TimeoutError` exceptions. 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.
5656

5757
```python
5858
import serpapi
5959

60-
client = serpapi.Client(api_key=os.getenv("API_KEY"))
60+
# A default timeout can be set here.
61+
client = serpapi.Client(api_key=os.getenv("API_KEY"), timeout=10)
6162

6263
try:
6364
results = client.search({
@@ -71,12 +72,17 @@ except serpapi.HTTPError as e:
7172
pass
7273
elif e.status_code == 429: # Exceeds the hourly throughput limit OR account run out of searches
7374
pass
75+
except serpapi.TimeoutError as e:
76+
# Handle timeout
77+
print(f"The request timed out: {e}")
7478
```
7579

7680
## Documentation
7781

7882
Documentation is [available on Read the Docs](https://serpapi-python.readthedocs.io/en/latest/).
7983

84+
Change history is [available on GitHub](https://github.com/serpapi/serpapi-python/blob/master/HISTORY.md).
85+
8086
## Basic Examples in Python
8187

8288
### Search Bing

README.md.erb

Lines changed: 0 additions & 165 deletions
This file was deleted.

serpapi/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.1.5"
1+
__version__ = "0.1.6"

serpapi/core.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ class Client(HTTPClient):
2525

2626
DASHBOARD_URL = "https://serpapi.com/dashboard"
2727

28+
def __init__(self, *, api_key=None, timeout=None):
29+
super().__init__(api_key=api_key, timeout=timeout)
30+
2831
def __repr__(self):
2932
return "<SerpApi Client>"
3033

@@ -60,10 +63,16 @@ def search(self, params: dict = None, **kwargs):
6063
if params is None:
6164
params = {}
6265

66+
# These are arguments that should be passed to the underlying requests.request call.
67+
request_kwargs = {}
68+
for key in ["timeout", "proxies", "verify", "stream", "cert"]:
69+
if key in kwargs:
70+
request_kwargs[key] = kwargs.pop(key)
71+
6372
if kwargs:
6473
params.update(kwargs)
6574

66-
r = self.request("GET", "/search", params=params)
75+
r = self.request("GET", "/search", params=params, **request_kwargs)
6776

6877
return SerpResults.from_http_response(r, client=self)
6978

@@ -80,6 +89,12 @@ def search_archive(self, params: dict = None, **kwargs):
8089
if params is None:
8190
params = {}
8291

92+
# These are arguments that should be passed to the underlying requests.request call.
93+
request_kwargs = {}
94+
for key in ["timeout", "proxies", "verify", "stream", "cert"]:
95+
if key in kwargs:
96+
request_kwargs[key] = kwargs.pop(key)
97+
8398
if kwargs:
8499
params.update(kwargs)
85100

@@ -90,7 +105,7 @@ def search_archive(self, params: dict = None, **kwargs):
90105
f"Please provide 'search_id', found here: { self.DASHBOARD_URL }"
91106
)
92107

93-
r = self.request("GET", f"/searches/{ search_id }", params=params)
108+
r = self.request("GET", f"/searches/{ search_id }", params=params, **request_kwargs)
94109
return SerpResults.from_http_response(r, client=self)
95110

96111
def locations(self, params: dict = None, **kwargs):
@@ -106,6 +121,12 @@ def locations(self, params: dict = None, **kwargs):
106121
if params is None:
107122
params = {}
108123

124+
# These are arguments that should be passed to the underlying requests.request call.
125+
request_kwargs = {}
126+
for key in ["timeout", "proxies", "verify", "stream", "cert"]:
127+
if key in kwargs:
128+
request_kwargs[key] = kwargs.pop(key)
129+
109130
if kwargs:
110131
params.update(kwargs)
111132

@@ -114,6 +135,7 @@ def locations(self, params: dict = None, **kwargs):
114135
"/locations.json",
115136
params=params,
116137
assert_200=True,
138+
**request_kwargs,
117139
)
118140
return r.json()
119141

@@ -129,10 +151,16 @@ def account(self, params: dict = None, **kwargs):
129151
if params is None:
130152
params = {}
131153

154+
# These are arguments that should be passed to the underlying requests.request call.
155+
request_kwargs = {}
156+
for key in ["timeout", "proxies", "verify", "stream", "cert"]:
157+
if key in kwargs:
158+
request_kwargs[key] = kwargs.pop(key)
159+
132160
if kwargs:
133161
params.update(kwargs)
134162

135-
r = self.request("GET", "/account.json", params=params, assert_200=True)
163+
r = self.request("GET", "/account.json", params=params, assert_200=True, **request_kwargs)
136164
return r.json()
137165

138166

serpapi/exceptions.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,9 @@ class HTTPConnectionError(HTTPError, requests.exceptions.ConnectionError, SerpAp
4343
"""Connection Error."""
4444

4545
pass
46+
47+
48+
class TimeoutError(requests.exceptions.Timeout, SerpApiError):
49+
"""Timeout Error."""
50+
51+
pass

serpapi/http.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from .exceptions import (
44
HTTPError,
55
HTTPConnectionError,
6+
TimeoutError,
67
)
78
from .__version__ import __version__
89

@@ -13,10 +14,11 @@ class HTTPClient:
1314
BASE_DOMAIN = "https://serpapi.com"
1415
USER_AGENT = f"serpapi-python, v{__version__}"
1516

16-
def __init__(self, *, api_key=None):
17+
def __init__(self, *, api_key=None, timeout=None):
1718
# Used to authenticate requests.
1819
# TODO: do we want to support the environment variable? Seems like a security risk.
1920
self.api_key = api_key
21+
self.timeout = timeout
2022
self.session = requests.Session()
2123

2224
def request(self, method, path, params, *, assert_200=True, **kwargs):
@@ -34,12 +36,18 @@ def request(self, method, path, params, *, assert_200=True, **kwargs):
3436
try:
3537
headers = {"User-Agent": self.USER_AGENT}
3638

39+
# Use the default timeout if one was provided to the client.
40+
if self.timeout and "timeout" not in kwargs:
41+
kwargs["timeout"] = self.timeout
42+
3743
r = self.session.request(
3844
method=method, url=url, params=params, headers=headers, **kwargs
3945
)
4046

4147
except requests.exceptions.ConnectionError as e:
4248
raise HTTPConnectionError(e)
49+
except requests.exceptions.Timeout as e:
50+
raise TimeoutError(e)
4351

4452
# Raise an exception if the status code is not 200.
4553
if assert_200:

0 commit comments

Comments
 (0)