yelpapi 2.7.0__tar.gz → 2.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {yelpapi-2.7.0 → yelpapi-2.8.0}/.github/workflows/publish.yml +5 -4
- {yelpapi-2.7.0 → yelpapi-2.8.0}/.github/workflows/tests.yml +25 -0
- {yelpapi-2.7.0 → yelpapi-2.8.0}/AGENTS.md +11 -5
- {yelpapi-2.7.0 → yelpapi-2.8.0}/CHANGES.md +18 -0
- {yelpapi-2.7.0 → yelpapi-2.8.0}/PKG-INFO +15 -4
- {yelpapi-2.7.0 → yelpapi-2.8.0}/README.md +12 -2
- {yelpapi-2.7.0 → yelpapi-2.8.0}/examples/examples.py +108 -32
- {yelpapi-2.7.0 → yelpapi-2.8.0}/pyproject.toml +26 -2
- yelpapi-2.8.0/release-notes.md +17 -0
- {yelpapi-2.7.0 → yelpapi-2.8.0}/tests/conftest.py +4 -1
- yelpapi-2.8.0/tests/test_yelpapi.py +539 -0
- yelpapi-2.8.0/yelpapi/yelpapi.py +632 -0
- yelpapi-2.7.0/release-notes.md +0 -6
- yelpapi-2.7.0/tests/test_yelpapi.py +0 -380
- yelpapi-2.7.0/yelpapi/yelpapi.py +0 -388
- {yelpapi-2.7.0 → yelpapi-2.8.0}/.gitignore +0 -0
- {yelpapi-2.7.0 → yelpapi-2.8.0}/AUTHORS.md +0 -0
- {yelpapi-2.7.0 → yelpapi-2.8.0}/LICENSE +0 -0
- {yelpapi-2.7.0 → yelpapi-2.8.0}/yelpapi/__init__.py +0 -0
- {yelpapi-2.7.0 → yelpapi-2.8.0}/yelpapi/py.typed +0 -0
|
@@ -16,15 +16,16 @@ jobs:
|
|
|
16
16
|
steps:
|
|
17
17
|
- uses: actions/checkout@v7
|
|
18
18
|
|
|
19
|
-
-
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
uses: actions/setup-python@v7
|
|
20
21
|
with:
|
|
21
22
|
python-version: '3.15'
|
|
22
23
|
|
|
23
24
|
- name: Verify tag matches package version
|
|
24
25
|
run: |
|
|
25
26
|
version="$(python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')"
|
|
26
|
-
if [ "v${version}" != "${
|
|
27
|
-
echo "Tag ${
|
|
27
|
+
if [ "v${version}" != "${GITHUB_REF_NAME}" ]; then
|
|
28
|
+
echo "Tag ${GITHUB_REF_NAME} does not match pyproject.toml version ${version}" >&2
|
|
28
29
|
exit 1
|
|
29
30
|
fi
|
|
30
31
|
|
|
@@ -48,4 +49,4 @@ jobs:
|
|
|
48
49
|
- name: Create GitHub release
|
|
49
50
|
env:
|
|
50
51
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
51
|
-
run: gh release create "${
|
|
52
|
+
run: gh release create "${GITHUB_REF_NAME}" dist/* --title "YelpAPI ${GITHUB_REF_NAME}" --notes-file release-notes.md
|
|
@@ -2,12 +2,36 @@ name: Tests
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
|
+
branches: [master]
|
|
5
6
|
pull_request:
|
|
6
7
|
|
|
7
8
|
permissions:
|
|
8
9
|
contents: read
|
|
9
10
|
|
|
11
|
+
# Cancel an in-progress run when a newer commit is pushed to the same branch or pull request.
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
10
16
|
jobs:
|
|
17
|
+
lint:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v7
|
|
22
|
+
|
|
23
|
+
# Ruff targets requires-python in pyproject.toml regardless of the interpreter it runs on.
|
|
24
|
+
- name: Set up Python
|
|
25
|
+
uses: actions/setup-python@v7
|
|
26
|
+
with:
|
|
27
|
+
python-version: '3.x'
|
|
28
|
+
|
|
29
|
+
- name: Install ruff
|
|
30
|
+
run: pip install ruff
|
|
31
|
+
|
|
32
|
+
- name: Lint with ruff
|
|
33
|
+
run: ruff check --output-format=github
|
|
34
|
+
|
|
11
35
|
typecheck:
|
|
12
36
|
runs-on: ubuntu-latest
|
|
13
37
|
|
|
@@ -31,6 +55,7 @@ jobs:
|
|
|
31
55
|
test:
|
|
32
56
|
runs-on: ubuntu-latest
|
|
33
57
|
strategy:
|
|
58
|
+
fail-fast: false
|
|
34
59
|
matrix:
|
|
35
60
|
python-version: ['3.11', '3.12', '3.13', '3.14', '3.15']
|
|
36
61
|
|
|
@@ -24,19 +24,24 @@ Type-check (strict; configured in `[tool.mypy]` in `pyproject.toml`, which sets
|
|
|
24
24
|
mypy
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Lint (configured in `[tool.ruff]` in `pyproject.toml`; `ruff check --fix` applies safe fixes):
|
|
28
|
+
```
|
|
29
|
+
ruff check
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
CI (`.github/workflows/tests.yml`) runs `pytest` on every supported Python version, plus `mypy` and `ruff check` once each in separate `typecheck` and `lint` jobs. All must pass.
|
|
28
33
|
|
|
29
34
|
## Architecture
|
|
30
35
|
|
|
31
|
-
This is a thin Python wrapper around the [Yelp
|
|
36
|
+
This is a thin Python wrapper around Yelp's public REST APIs: the [Yelp Places API](https://docs.developer.yelp.com/docs/places-intro), the [Yelp AI API](https://docs.developer.yelp.com/docs/yelp-ai-api), [Yelp Insights](https://docs.developer.yelp.com/docs/yelp-insights), and the [Home Services API](https://docs.developer.yelp.com/docs/home-services-api). The entire implementation lives in `yelpapi/yelpapi.py` as a single `YelpAPI` class, re-exported from `yelpapi/__init__.py` via `__all__`.
|
|
32
37
|
|
|
33
38
|
**Design philosophy:** The wrapper is intentionally minimal and extensible — all API parameters are passed through `**kwargs` directly to the HTTP request, so it doesn't break when Yelp adds new parameters. The only thing that would break it is a URL scheme change. Public methods return `dict[str, Any]`; don't introduce `TypedDict`s or response models, since they would break this pass-through design.
|
|
34
39
|
|
|
35
|
-
**Request flow:** Each public method (e.g., `search_query`, `business_query`) validates required parameters, then delegates to `
|
|
40
|
+
**Request flow:** Each public method (e.g., `search_query`, `business_query`) validates required parameters, then delegates to `_get()` (GET; kwargs become query string parameters) or `_post()` (POST; kwargs become the JSON body). Both strip `None`-valued kwargs before issuing the request via a shared `requests.Session`, then hand the response to `_parse_response()`, which raises `requests.HTTPError` for HTTP error statuses, returns `{}` for empty `202`/`204` responses, and raises `YelpAPI.YelpAPIError` if the JSON body isn't an object or contains an `error` key. Methods that require a location share a `_require_location_or_lat_lng()` helper.
|
|
36
41
|
|
|
37
42
|
**Session management:** A single `requests.Session` is reused for all calls (performance benefit). Users should close it via context manager (`with YelpAPI(...) as api`) or explicitly via `api.close()`.
|
|
38
43
|
|
|
39
|
-
**Tests** use `requests-mock` (via the `mock_request` autouse fixture in `conftest.py`) to intercept HTTP calls, and `Faker` for generating
|
|
44
|
+
**Tests** use `requests-mock` (via the `mock_request` autouse fixture in `conftest.py`) to intercept HTTP calls, and a fixed-seed `Faker` for generating test data, so every run is deterministic. No real API calls are made in tests.
|
|
40
45
|
|
|
41
46
|
## Conventions
|
|
42
47
|
|
|
@@ -45,7 +50,8 @@ This is a thin Python wrapper around the [Yelp Fusion API](https://docs.develope
|
|
|
45
50
|
- **Style:** Use single quotes for strings, and 120-character lines.
|
|
46
51
|
- **Adding an endpoint** requires all of the following:
|
|
47
52
|
- a `*_API_URL` constant
|
|
48
|
-
- a method that validates required parameters and calls `
|
|
53
|
+
- a method that validates required parameters and calls `_get()` (GET) or `_post()` (POST); path parameters are positional arguments inserted with `quote(value, safe='')`
|
|
54
|
+
- an entry in the `GET_ENDPOINTS` or `POST_ENDPOINTS` table in `tests/test_yelpapi.py` (use `PATH_PARAM` for path parameters)
|
|
49
55
|
- an entry in the `YelpAPI` class docstring's API list
|
|
50
56
|
- an entry in the README "METHODS" list
|
|
51
57
|
- tests covering both validation failures and success
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2.8.0 (2026-10-28)
|
|
4
|
+
* **Heads up for subclassers:** the private request helper `YelpAPI._query()` has been renamed to `YelpAPI._get()`, and the new POST helper is `YelpAPI._post()`.
|
|
5
|
+
* If your subclass *calls* `self._query(...)` to reach endpoints this library doesn't wrap, switch to `self._get(...)`. `_query()` still works for this release but emits a `FutureWarning`.
|
|
6
|
+
* If your subclass *overrides* `_query()` (e.g., for caching, retries, or logging), override `_get()` (and `_post()` for POST endpoints) instead. Public methods no longer call `_query()`, so an override of it is no longer used. Defining such a subclass emits a `FutureWarning`.
|
|
7
|
+
* `_query()` will be removed in 2.9.0.
|
|
8
|
+
* Added 8 new API endpoints: AI Chat (`ai_chat_query`), Business Insights (`business_insights_query`), Business Food & Drinks Insights (`business_food_and_drinks_insights_query`), Business Risk Signal Insights (`business_risk_signals_insights_query`), Business Request a Phone Call (`business_rapc_enabledness_query`), Jobs (`jobs_query`), Job Survey (`job_survey_query`), and Request a Quote (`request_a_quote_query`).
|
|
9
|
+
* Added support for POST endpoints, which send `**kwargs` as the JSON request body (with `None` values removed). Successful responses with no body (e.g., `202 Accepted`) return an empty dict.
|
|
10
|
+
* `business_service_offerings_query` now emits a `DeprecationWarning`, because Yelp has deprecated the endpoint in favor of "Jobs offered" on the Business API.
|
|
11
|
+
* Fixed: a latitude or longitude of `0` (the equator or prime meridian) is now accepted by `search_query`, `featured_event_query`, and `transaction_search_query` instead of raising `ValueError`.
|
|
12
|
+
* Fixed: `business_match_query` now accepts an empty `address1`, which Yelp allows for service businesses with no street address.
|
|
13
|
+
* Fixed: a malformed `error` object in a response (e.g., a plain string, or one missing `code` or `description`) now raises `YelpAPI.YelpAPIError` instead of a `TypeError` or `KeyError`.
|
|
14
|
+
* Path parameters (business IDs, aliases, event IDs, job aliases, and transaction types) are now percent-encoded, so characters like `/`, `?`, and `#` can no longer change the URL that is requested.
|
|
15
|
+
* Clarified the `YelpAPI.YelpAPIError` docs: HTTP error statuses raise `requests.HTTPError`, while `YelpAPIError` covers error objects and non-object bodies in successful responses.
|
|
16
|
+
* Updated docstrings to match Yelp's current docs (plan and access requirements, `match_threshold`, the 20-ID limit for `business_ids`, and required fields for Request a Quote).
|
|
17
|
+
* Fixed the examples so they catch `requests.HTTPError` and keep going when an endpoint needs a paid plan or extra access.
|
|
18
|
+
* Strengthened the test suite: every endpoint's URL, HTTP method, and parameters are now checked, and test data is deterministic.
|
|
19
|
+
* Added [Ruff](https://docs.astral.sh/ruff/) linting, run in CI.
|
|
20
|
+
|
|
3
21
|
## 2.7.0 (2026-10-28)
|
|
4
22
|
* Dropped support for end-of-life Python 3.9 and 3.10; added Python 3.15. Minimum supported version is now 3.11.
|
|
5
23
|
* `YelpAPI.__enter__` now returns `Self`, so subclasses are typed correctly when used as context managers.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: yelpapi
|
|
3
|
-
Version: 2.
|
|
4
|
-
Summary: yelpapi is a pure Python implementation of
|
|
3
|
+
Version: 2.8.0
|
|
4
|
+
Summary: yelpapi is a pure Python implementation of Yelp's public REST APIs (Places, AI, Insights, and Home Services).
|
|
5
5
|
Project-URL: Homepage, https://github.com/lanl/yelpapi
|
|
6
6
|
Project-URL: Changelog, https://github.com/lanl/yelpapi/blob/master/CHANGES.md
|
|
7
7
|
Author-email: Geoffrey Fairchild <mail@gfairchild.com>
|
|
@@ -49,6 +49,7 @@ Requires-Dist: mypy; extra == 'dev'
|
|
|
49
49
|
Requires-Dist: pytest; extra == 'dev'
|
|
50
50
|
Requires-Dist: pytest-cov; extra == 'dev'
|
|
51
51
|
Requires-Dist: requests-mock; extra == 'dev'
|
|
52
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
52
53
|
Requires-Dist: types-requests; extra == 'dev'
|
|
53
54
|
Description-Content-Type: text/markdown
|
|
54
55
|
|
|
@@ -60,7 +61,7 @@ Description-Content-Type: text/markdown
|
|
|
60
61
|
This software is licensed under the [BSD 3-Clause License](https://opensource.org/licenses/BSD-3-Clause). Please refer to the separate [LICENSE](LICENSE) file for the exact text of the license. You are obligated to give attribution if you use this code.
|
|
61
62
|
|
|
62
63
|
## ABOUT
|
|
63
|
-
yelpapi is a pure Python implementation of the [Yelp
|
|
64
|
+
yelpapi is a pure Python implementation of Yelp's public REST APIs, including the [Yelp Places API](https://docs.developer.yelp.com/docs/places-intro), the [Yelp AI API](https://docs.developer.yelp.com/docs/yelp-ai-api), [Yelp Insights](https://docs.developer.yelp.com/docs/yelp-insights), and the [Home Services API](https://docs.developer.yelp.com/docs/home-services-api). It is simple, fast, and robust to any changes Yelp may make to its APIs in the future.
|
|
64
65
|
|
|
65
66
|
## REQUIREMENTS
|
|
66
67
|
This code requires Python 3.11 or higher and [requests](https://github.com/psf/requests).
|
|
@@ -101,18 +102,28 @@ finally:
|
|
|
101
102
|
```
|
|
102
103
|
|
|
103
104
|
## METHODS
|
|
105
|
+
* [AI Chat API](https://docs.developer.yelp.com/reference/v2_ai_chat) - `ai_chat_query(...)`
|
|
104
106
|
* [Autocomplete API](https://docs.developer.yelp.com/reference/v3_autocomplete) - `autocomplete_query(...)`
|
|
105
107
|
* [Business API](https://docs.developer.yelp.com/reference/v3_business_info) - `business_query(...)`
|
|
106
108
|
* [Business Engagement Metrics API](https://docs.developer.yelp.com/reference/v3_get_businesses_engagement) - `business_engagement_query(...)`
|
|
109
|
+
* [Business Food & Drinks Insights API](https://docs.developer.yelp.com/reference/v3_get_business_food_and_drinks_insights) - `business_food_and_drinks_insights_query(...)`
|
|
110
|
+
* [Business Insights API](https://docs.developer.yelp.com/reference/v3_businesses_insights) - `business_insights_query(...)`
|
|
107
111
|
* [Business Match API](https://docs.developer.yelp.com/reference/v3_business_match) - `business_match_query(...)`
|
|
108
|
-
* [Business
|
|
112
|
+
* [Business Request a Phone Call API](https://docs.developer.yelp.com/reference/v3_business_rapc_enabledness) - `business_rapc_enabledness_query(...)`
|
|
113
|
+
* [Business Risk Signal Insights API](https://docs.developer.yelp.com/reference/v3_get_business_risk_signals_insights) - `business_risk_signals_insights_query(...)`
|
|
114
|
+
* [Business Service Offerings API](https://docs.developer.yelp.com/reference/v3_business_service_offerings) - `business_service_offerings_query(...)` (deprecated by Yelp in favor of "Jobs offered" on the Business API)
|
|
109
115
|
* [Categories API](https://docs.developer.yelp.com/reference/v3_all_categories) - `categories_query(...)`
|
|
110
116
|
* [Category API](https://docs.developer.yelp.com/reference/v3_categories) - `category_query(...)`
|
|
111
117
|
* [Event Lookup API](https://docs.developer.yelp.com/reference/v3_event) - `event_lookup_query(...)`
|
|
112
118
|
* [Event Search API](https://docs.developer.yelp.com/reference/v3_events_search) - `event_search_query(...)`
|
|
113
119
|
* [Featured Event API](https://docs.developer.yelp.com/reference/v3_featured_event) - `featured_event_query(...)`
|
|
120
|
+
* [Job Survey API](https://docs.developer.yelp.com/reference/v3_job_survey) - `job_survey_query(...)`
|
|
121
|
+
* [Jobs API](https://docs.developer.yelp.com/reference/v3_get_jobs) - `jobs_query(...)`
|
|
114
122
|
* [Phone Search API](https://docs.developer.yelp.com/reference/v3_business_phone_search) - `phone_search_query(...)`
|
|
123
|
+
* [Request a Quote API](https://docs.developer.yelp.com/reference/v3_raq_submit_project) - `request_a_quote_query(...)`
|
|
115
124
|
* [Review Highlights API](https://docs.developer.yelp.com/reference/v3_business_review_highlights) - `review_highlights_query(...)`
|
|
116
125
|
* [Reviews API](https://docs.developer.yelp.com/reference/v3_business_reviews) - `reviews_query(...)`
|
|
117
126
|
* [Search API](https://docs.developer.yelp.com/reference/v3_business_search) - `search_query(...)`
|
|
118
127
|
* [Transaction Search API](https://docs.developer.yelp.com/reference/v3_transaction_search) - `transaction_search_query(...)`
|
|
128
|
+
|
|
129
|
+
Yelp's [GraphQL API](https://docs.developer.yelp.com/docs/graphql-intro) is not wrapped, since it uses GraphQL queries rather than REST parameters.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
This software is licensed under the [BSD 3-Clause License](https://opensource.org/licenses/BSD-3-Clause). Please refer to the separate [LICENSE](LICENSE) file for the exact text of the license. You are obligated to give attribution if you use this code.
|
|
7
7
|
|
|
8
8
|
## ABOUT
|
|
9
|
-
yelpapi is a pure Python implementation of the [Yelp
|
|
9
|
+
yelpapi is a pure Python implementation of Yelp's public REST APIs, including the [Yelp Places API](https://docs.developer.yelp.com/docs/places-intro), the [Yelp AI API](https://docs.developer.yelp.com/docs/yelp-ai-api), [Yelp Insights](https://docs.developer.yelp.com/docs/yelp-insights), and the [Home Services API](https://docs.developer.yelp.com/docs/home-services-api). It is simple, fast, and robust to any changes Yelp may make to its APIs in the future.
|
|
10
10
|
|
|
11
11
|
## REQUIREMENTS
|
|
12
12
|
This code requires Python 3.11 or higher and [requests](https://github.com/psf/requests).
|
|
@@ -47,18 +47,28 @@ finally:
|
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
## METHODS
|
|
50
|
+
* [AI Chat API](https://docs.developer.yelp.com/reference/v2_ai_chat) - `ai_chat_query(...)`
|
|
50
51
|
* [Autocomplete API](https://docs.developer.yelp.com/reference/v3_autocomplete) - `autocomplete_query(...)`
|
|
51
52
|
* [Business API](https://docs.developer.yelp.com/reference/v3_business_info) - `business_query(...)`
|
|
52
53
|
* [Business Engagement Metrics API](https://docs.developer.yelp.com/reference/v3_get_businesses_engagement) - `business_engagement_query(...)`
|
|
54
|
+
* [Business Food & Drinks Insights API](https://docs.developer.yelp.com/reference/v3_get_business_food_and_drinks_insights) - `business_food_and_drinks_insights_query(...)`
|
|
55
|
+
* [Business Insights API](https://docs.developer.yelp.com/reference/v3_businesses_insights) - `business_insights_query(...)`
|
|
53
56
|
* [Business Match API](https://docs.developer.yelp.com/reference/v3_business_match) - `business_match_query(...)`
|
|
54
|
-
* [Business
|
|
57
|
+
* [Business Request a Phone Call API](https://docs.developer.yelp.com/reference/v3_business_rapc_enabledness) - `business_rapc_enabledness_query(...)`
|
|
58
|
+
* [Business Risk Signal Insights API](https://docs.developer.yelp.com/reference/v3_get_business_risk_signals_insights) - `business_risk_signals_insights_query(...)`
|
|
59
|
+
* [Business Service Offerings API](https://docs.developer.yelp.com/reference/v3_business_service_offerings) - `business_service_offerings_query(...)` (deprecated by Yelp in favor of "Jobs offered" on the Business API)
|
|
55
60
|
* [Categories API](https://docs.developer.yelp.com/reference/v3_all_categories) - `categories_query(...)`
|
|
56
61
|
* [Category API](https://docs.developer.yelp.com/reference/v3_categories) - `category_query(...)`
|
|
57
62
|
* [Event Lookup API](https://docs.developer.yelp.com/reference/v3_event) - `event_lookup_query(...)`
|
|
58
63
|
* [Event Search API](https://docs.developer.yelp.com/reference/v3_events_search) - `event_search_query(...)`
|
|
59
64
|
* [Featured Event API](https://docs.developer.yelp.com/reference/v3_featured_event) - `featured_event_query(...)`
|
|
65
|
+
* [Job Survey API](https://docs.developer.yelp.com/reference/v3_job_survey) - `job_survey_query(...)`
|
|
66
|
+
* [Jobs API](https://docs.developer.yelp.com/reference/v3_get_jobs) - `jobs_query(...)`
|
|
60
67
|
* [Phone Search API](https://docs.developer.yelp.com/reference/v3_business_phone_search) - `phone_search_query(...)`
|
|
68
|
+
* [Request a Quote API](https://docs.developer.yelp.com/reference/v3_raq_submit_project) - `request_a_quote_query(...)`
|
|
61
69
|
* [Review Highlights API](https://docs.developer.yelp.com/reference/v3_business_review_highlights) - `review_highlights_query(...)`
|
|
62
70
|
* [Reviews API](https://docs.developer.yelp.com/reference/v3_business_reviews) - `reviews_query(...)`
|
|
63
71
|
* [Search API](https://docs.developer.yelp.com/reference/v3_business_search) - `search_query(...)`
|
|
64
72
|
* [Transaction Search API](https://docs.developer.yelp.com/reference/v3_transaction_search) - `transaction_search_query(...)`
|
|
73
|
+
|
|
74
|
+
Yelp's [GraphQL API](https://docs.developer.yelp.com/docs/graphql-intro) is not wrapped, since it uses GraphQL queries rather than REST parameters.
|
|
@@ -25,19 +25,47 @@
|
|
|
25
25
|
|
|
26
26
|
# Example call:
|
|
27
27
|
# ./examples.py "[API Key]"
|
|
28
|
+
#
|
|
29
|
+
# Some endpoints below require a paid plan or access that must be requested from Yelp. Those calls are made via
|
|
30
|
+
# run_restricted(), which prints the HTTP error and moves on instead of aborting the script.
|
|
28
31
|
|
|
29
32
|
import argparse
|
|
33
|
+
import contextlib
|
|
34
|
+
from collections.abc import Callable
|
|
30
35
|
from pprint import pprint
|
|
36
|
+
from typing import Any
|
|
37
|
+
|
|
38
|
+
import requests
|
|
31
39
|
|
|
32
40
|
from yelpapi import YelpAPI
|
|
33
41
|
|
|
34
42
|
SEP = '\n-------------------------------------------------------------------------\n'
|
|
35
43
|
|
|
44
|
+
|
|
45
|
+
def print_http_error(e: requests.HTTPError) -> None:
|
|
46
|
+
"""Print an HTTP error, including Yelp's error object if the response has one."""
|
|
47
|
+
print(e)
|
|
48
|
+
if e.response is not None:
|
|
49
|
+
with contextlib.suppress(ValueError):
|
|
50
|
+
print(e.response.json().get('error'))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def run_restricted(call: Callable[[], dict[str, Any]]) -> dict[str, Any] | None:
|
|
54
|
+
"""Run a call that may fail for API keys without the required plan or access."""
|
|
55
|
+
try:
|
|
56
|
+
response = call()
|
|
57
|
+
except requests.HTTPError as e:
|
|
58
|
+
print('(this endpoint may require a paid plan or extra access for your API key)')
|
|
59
|
+
print_http_error(e)
|
|
60
|
+
return None
|
|
61
|
+
pprint(response)
|
|
62
|
+
return response
|
|
63
|
+
|
|
36
64
|
argparser = argparse.ArgumentParser(
|
|
37
65
|
description='Example Yelp queries using yelpapi. '
|
|
38
|
-
'Visit https://docs.developer.yelp.com/docs/
|
|
66
|
+
'Visit https://docs.developer.yelp.com/docs/places-intro to get the necessary API keys.',
|
|
39
67
|
)
|
|
40
|
-
argparser.add_argument('api_key', type=str, help='Yelp
|
|
68
|
+
argparser.add_argument('api_key', type=str, help='Yelp API key')
|
|
41
69
|
args = argparser.parse_args()
|
|
42
70
|
|
|
43
71
|
with YelpAPI(args.api_key) as yelp_api:
|
|
@@ -74,6 +102,7 @@ with YelpAPI(args.api_key) as yelp_api:
|
|
|
74
102
|
print(SEP)
|
|
75
103
|
|
|
76
104
|
# Transaction Search API: https://docs.developer.yelp.com/reference/v3_transaction_search
|
|
105
|
+
# NOTE: 'delivery' is the only supported transaction type, and only US locations are supported
|
|
77
106
|
print("***** businesses in Dallas supporting delivery transactions *****\nyelp_api.transaction_search_query(transaction_type='delivery', location='dallas, tx')\n")
|
|
78
107
|
response = yelp_api.transaction_search_query(transaction_type='delivery', location='dallas, tx')
|
|
79
108
|
pprint(response)
|
|
@@ -85,19 +114,6 @@ with YelpAPI(args.api_key) as yelp_api:
|
|
|
85
114
|
pprint(response)
|
|
86
115
|
print(SEP)
|
|
87
116
|
|
|
88
|
-
# Reviews API: https://docs.developer.yelp.com/reference/v3_business_reviews
|
|
89
|
-
print("***** selected reviews for Amy's on 6th St. *****\nyelp_api.reviews_query(id='amys-ice-creams-austin-3')\n")
|
|
90
|
-
response = yelp_api.reviews_query(id='amys-ice-creams-austin-3')
|
|
91
|
-
pprint(response)
|
|
92
|
-
print(SEP)
|
|
93
|
-
|
|
94
|
-
# Review Highlights API: https://docs.developer.yelp.com/reference/v3_business_review_highlights
|
|
95
|
-
# NOTE: requires a Premium Plan
|
|
96
|
-
print("***** review highlights for Amy's on 6th St. *****\nyelp_api.review_highlights_query(id='amys-ice-creams-austin-3')\n")
|
|
97
|
-
response = yelp_api.review_highlights_query(id='amys-ice-creams-austin-3')
|
|
98
|
-
pprint(response)
|
|
99
|
-
print(SEP)
|
|
100
|
-
|
|
101
117
|
# Autocomplete API: https://docs.developer.yelp.com/reference/v3_autocomplete
|
|
102
118
|
# centroid: https://www.flickr.com/places/info/2427422
|
|
103
119
|
print("***** autocomplete results for 'Hambur' in Iowa City *****\nyelp_api.autocomplete_query(text='Hambur', longitude=-91.5327, latitude=41.6560)\n")
|
|
@@ -106,7 +122,7 @@ with YelpAPI(args.api_key) as yelp_api:
|
|
|
106
122
|
print(SEP)
|
|
107
123
|
|
|
108
124
|
# Categories API: https://docs.developer.yelp.com/reference/v3_all_categories
|
|
109
|
-
print(
|
|
125
|
+
print('***** all Yelp categories *****\nyelp_api.categories_query()\n')
|
|
110
126
|
response = yelp_api.categories_query()
|
|
111
127
|
pprint(response)
|
|
112
128
|
print(SEP)
|
|
@@ -118,16 +134,17 @@ with YelpAPI(args.api_key) as yelp_api:
|
|
|
118
134
|
print(SEP)
|
|
119
135
|
|
|
120
136
|
# Event Search API: https://docs.developer.yelp.com/reference/v3_events_search
|
|
121
|
-
print("***** event search result *****\nyelp_api.event_search_query()\n")
|
|
122
|
-
response = yelp_api.event_search_query()
|
|
137
|
+
print("***** event search result for New York City, NY *****\nyelp_api.event_search_query(location='New York City, NY')\n")
|
|
138
|
+
response = yelp_api.event_search_query(location='New York City, NY')
|
|
123
139
|
pprint(response)
|
|
124
140
|
print(SEP)
|
|
125
141
|
|
|
126
142
|
# Event Lookup API: https://docs.developer.yelp.com/reference/v3_event
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
143
|
+
if response.get('events'):
|
|
144
|
+
print("***** event lookup result using previous search's first event *****\nyelp_api.event_lookup_query(id=response['events'][0]['id'])\n")
|
|
145
|
+
response = yelp_api.event_lookup_query(id=response['events'][0]['id'])
|
|
146
|
+
pprint(response)
|
|
147
|
+
print(SEP)
|
|
131
148
|
|
|
132
149
|
# Featured Event API: https://docs.developer.yelp.com/reference/v3_featured_event
|
|
133
150
|
print("***** featured event lookup result for New York City, NY *****\nyelp_api.featured_event_query(location='New York City, NY')\n")
|
|
@@ -135,32 +152,91 @@ with YelpAPI(args.api_key) as yelp_api:
|
|
|
135
152
|
pprint(response)
|
|
136
153
|
print(SEP)
|
|
137
154
|
|
|
155
|
+
# AI Chat API: https://docs.developer.yelp.com/reference/v2_ai_chat
|
|
156
|
+
print("***** AI chat about ice cream in Austin, TX *****\nyelp_api.ai_chat_query(query='Where can I get great ice cream in Austin, TX?')\n")
|
|
157
|
+
run_restricted(lambda: yelp_api.ai_chat_query(query='Where can I get great ice cream in Austin, TX?'))
|
|
158
|
+
print(SEP)
|
|
159
|
+
|
|
160
|
+
# ----- The endpoints below require a paid plan or extra access. -----
|
|
161
|
+
|
|
162
|
+
# Reviews API: https://docs.developer.yelp.com/reference/v3_business_reviews
|
|
163
|
+
# NOTE: requires an Enhanced or Premium Plan
|
|
164
|
+
print("***** selected reviews for Amy's on 6th St. *****\nyelp_api.reviews_query(id='amys-ice-creams-austin-3')\n")
|
|
165
|
+
run_restricted(lambda: yelp_api.reviews_query(id='amys-ice-creams-austin-3'))
|
|
166
|
+
print(SEP)
|
|
167
|
+
|
|
168
|
+
# Review Highlights API: https://docs.developer.yelp.com/reference/v3_business_review_highlights
|
|
169
|
+
# NOTE: requires a Premium Plan
|
|
170
|
+
print("***** review highlights for Amy's on 6th St. *****\nyelp_api.review_highlights_query(id='amys-ice-creams-austin-3')\n")
|
|
171
|
+
run_restricted(lambda: yelp_api.review_highlights_query(id='amys-ice-creams-austin-3'))
|
|
172
|
+
print(SEP)
|
|
173
|
+
|
|
138
174
|
# Business Engagement Metrics API: https://docs.developer.yelp.com/reference/v3_get_businesses_engagement
|
|
139
|
-
# NOTE:
|
|
175
|
+
# NOTE: part of Yelp Insights; requires Yelp Insights access for your API key
|
|
140
176
|
print("***** engagement metrics for Amy's on 6th St. *****\nyelp_api.business_engagement_query(business_ids='amys-ice-creams-austin-3')\n")
|
|
141
|
-
|
|
142
|
-
pprint(response)
|
|
177
|
+
run_restricted(lambda: yelp_api.business_engagement_query(business_ids='amys-ice-creams-austin-3'))
|
|
143
178
|
print(SEP)
|
|
144
179
|
|
|
145
180
|
# Business Service Offerings API: https://docs.developer.yelp.com/reference/v3_business_service_offerings
|
|
146
|
-
# NOTE: requires special permissions on the Yelp
|
|
181
|
+
# NOTE: requires special permissions on the Yelp API key
|
|
182
|
+
# DEPRECATED: Yelp recommends "Jobs offered" on the Business API instead
|
|
147
183
|
print("***** service offerings for Amy's on 6th St. *****\nyelp_api.business_service_offerings_query(id='amys-ice-creams-austin-3')\n")
|
|
148
|
-
|
|
149
|
-
|
|
184
|
+
run_restricted(lambda: yelp_api.business_service_offerings_query(id='amys-ice-creams-austin-3'))
|
|
185
|
+
print(SEP)
|
|
186
|
+
|
|
187
|
+
# Business Insights API: https://docs.developer.yelp.com/reference/v3_businesses_insights
|
|
188
|
+
# NOTE: part of Yelp Insights; requires Yelp Insights access for your API key
|
|
189
|
+
# date_range_start/date_range_end are months in YYYYMM format
|
|
190
|
+
print("***** business insights for Amy's on 6th St. *****\nyelp_api.business_insights_query(business_ids='amys-ice-creams-austin-3', date_range_start='202601', date_range_end='202603')\n")
|
|
191
|
+
run_restricted(lambda: yelp_api.business_insights_query(
|
|
192
|
+
business_ids='amys-ice-creams-austin-3',
|
|
193
|
+
date_range_start='202601',
|
|
194
|
+
date_range_end='202603',
|
|
195
|
+
))
|
|
150
196
|
print(SEP)
|
|
151
197
|
|
|
198
|
+
# Business Food & Drinks Insights API: https://docs.developer.yelp.com/reference/v3_get_business_food_and_drinks_insights
|
|
199
|
+
# NOTE: part of Yelp Insights; requires Yelp Insights access for your API key
|
|
200
|
+
print("***** food & drinks insights for Amy's on 6th St. *****\nyelp_api.business_food_and_drinks_insights_query(id='amys-ice-creams-austin-3')\n")
|
|
201
|
+
run_restricted(lambda: yelp_api.business_food_and_drinks_insights_query(id='amys-ice-creams-austin-3'))
|
|
202
|
+
print(SEP)
|
|
203
|
+
|
|
204
|
+
# Business Risk Signal Insights API: https://docs.developer.yelp.com/reference/v3_get_business_risk_signals_insights
|
|
205
|
+
# NOTE: part of Yelp Insights; requires Yelp Insights access for your API key
|
|
206
|
+
print("***** risk signal insights for Amy's on 6th St. *****\nyelp_api.business_risk_signals_insights_query(id='amys-ice-creams-austin-3')\n")
|
|
207
|
+
run_restricted(lambda: yelp_api.business_risk_signals_insights_query(id='amys-ice-creams-austin-3'))
|
|
208
|
+
print(SEP)
|
|
209
|
+
|
|
210
|
+
# Jobs API: https://docs.developer.yelp.com/reference/v3_get_jobs
|
|
211
|
+
# NOTE: part of the Home Services API; access is disabled by default and must be requested from Yelp
|
|
212
|
+
print("***** home service jobs matching a leaky faucet *****\nyelp_api.jobs_query(query='I need someone to fix a leaky faucet')\n")
|
|
213
|
+
jobs = run_restricted(lambda: yelp_api.jobs_query(query='I need someone to fix a leaky faucet'))
|
|
214
|
+
print(SEP)
|
|
215
|
+
|
|
216
|
+
# Job Survey API: https://docs.developer.yelp.com/reference/v3_job_survey
|
|
217
|
+
# NOTE: part of the Home Services API; access is disabled by default and must be requested from Yelp
|
|
218
|
+
if jobs and jobs.get('jobs'):
|
|
219
|
+
job_alias = jobs['jobs'][0]['job_alias']
|
|
220
|
+
print(f"***** survey for the best-matching job from the previous query *****\nyelp_api.job_survey_query(job_alias='{job_alias}')\n")
|
|
221
|
+
run_restricted(lambda: yelp_api.job_survey_query(job_alias=job_alias))
|
|
222
|
+
print(SEP)
|
|
223
|
+
|
|
224
|
+
# ----- Error handling -----
|
|
225
|
+
# HTTP error statuses (e.g., 400 for bad parameters, 404 for unknown IDs) raise requests.HTTPError.
|
|
226
|
+
# YelpAPI.YelpAPIError is raised when a successful HTTP response contains a Yelp error object or isn't a JSON object.
|
|
227
|
+
|
|
152
228
|
# Example erroneous search query
|
|
153
229
|
print("***** sample erroneous search query *****\nyelp_api.search_query(term='ice cream', location='austin, tx', sort_by='BAD_SORT')\n")
|
|
154
230
|
try:
|
|
155
231
|
# sort can only take on values "best_match", "rating", "review_count", or "distance"
|
|
156
232
|
yelp_api.search_query(term='ice cream', location='austin, tx', sort_by='BAD_SORT')
|
|
157
|
-
except
|
|
158
|
-
|
|
233
|
+
except requests.HTTPError as e:
|
|
234
|
+
print_http_error(e)
|
|
159
235
|
print(SEP)
|
|
160
236
|
|
|
161
237
|
# Example erroneous business query
|
|
162
238
|
print("***** sample erroneous business query *****\nyelp_api.business_query(id='fake-business')\n")
|
|
163
239
|
try:
|
|
164
240
|
yelp_api.business_query(id='fake-business')
|
|
165
|
-
except
|
|
166
|
-
|
|
241
|
+
except requests.HTTPError as e:
|
|
242
|
+
print_http_error(e)
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "yelpapi"
|
|
7
|
-
version = "2.
|
|
8
|
-
description = "yelpapi is a pure Python implementation of
|
|
7
|
+
version = "2.8.0"
|
|
8
|
+
description = "yelpapi is a pure Python implementation of Yelp's public REST APIs (Places, AI, Insights, and Home Services)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {file = "LICENSE"}
|
|
11
11
|
authors = [
|
|
@@ -39,6 +39,7 @@ dev = [
|
|
|
39
39
|
"pytest",
|
|
40
40
|
"pytest-cov",
|
|
41
41
|
"requests-mock",
|
|
42
|
+
"ruff",
|
|
42
43
|
"types-requests",
|
|
43
44
|
]
|
|
44
45
|
|
|
@@ -49,6 +50,29 @@ Changelog = "https://github.com/lanl/yelpapi/blob/master/CHANGES.md"
|
|
|
49
50
|
[tool.pytest.ini_options]
|
|
50
51
|
addopts = "-vl --cov=yelpapi --import-mode=importlib"
|
|
51
52
|
|
|
53
|
+
[tool.ruff]
|
|
54
|
+
line-length = 120
|
|
55
|
+
|
|
56
|
+
[tool.ruff.lint]
|
|
57
|
+
select = [
|
|
58
|
+
"E", # pycodestyle errors
|
|
59
|
+
"W", # pycodestyle warnings
|
|
60
|
+
"F", # pyflakes
|
|
61
|
+
"I", # isort
|
|
62
|
+
"B", # flake8-bugbear
|
|
63
|
+
"UP", # pyupgrade
|
|
64
|
+
"SIM", # flake8-simplify
|
|
65
|
+
"Q", # flake8-quotes
|
|
66
|
+
"RUF", # ruff-specific rules
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
[tool.ruff.lint.flake8-quotes]
|
|
70
|
+
inline-quotes = "single"
|
|
71
|
+
|
|
72
|
+
[tool.ruff.lint.per-file-ignores]
|
|
73
|
+
# The examples print each call verbatim on one line.
|
|
74
|
+
"examples/*" = ["E501"]
|
|
75
|
+
|
|
52
76
|
[tool.mypy]
|
|
53
77
|
strict = true
|
|
54
78
|
python_version = "3.11"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
* **Heads up for subclassers:** the private request helper `YelpAPI._query()` has been renamed to `YelpAPI._get()`, and the new POST helper is `YelpAPI._post()`.
|
|
2
|
+
* If your subclass *calls* `self._query(...)` to reach endpoints this library doesn't wrap, switch to `self._get(...)`. `_query()` still works for this release but emits a `FutureWarning`.
|
|
3
|
+
* If your subclass *overrides* `_query()` (e.g., for caching, retries, or logging), override `_get()` (and `_post()` for POST endpoints) instead. Public methods no longer call `_query()`, so an override of it is no longer used. Defining such a subclass emits a `FutureWarning`.
|
|
4
|
+
* `_query()` will be removed in 2.9.0.
|
|
5
|
+
* Added 8 new API endpoints: AI Chat (`ai_chat_query`), Business Insights (`business_insights_query`), Business Food & Drinks Insights (`business_food_and_drinks_insights_query`), Business Risk Signal Insights (`business_risk_signals_insights_query`), Business Request a Phone Call (`business_rapc_enabledness_query`), Jobs (`jobs_query`), Job Survey (`job_survey_query`), and Request a Quote (`request_a_quote_query`).
|
|
6
|
+
* Added support for POST endpoints, which send `**kwargs` as the JSON request body (with `None` values removed). Successful responses with no body (e.g., `202 Accepted`) return an empty dict.
|
|
7
|
+
* `business_service_offerings_query` now emits a `DeprecationWarning`, because Yelp has deprecated the endpoint in favor of "Jobs offered" on the Business API.
|
|
8
|
+
* Fixed: a latitude or longitude of `0` (the equator or prime meridian) is now accepted by `search_query`, `featured_event_query`, and `transaction_search_query` instead of raising `ValueError`.
|
|
9
|
+
* Fixed: `business_match_query` now accepts an empty `address1`, which Yelp allows for service businesses with no street address.
|
|
10
|
+
* Fixed: a malformed `error` object in a response (e.g., a plain string, or one missing `code` or `description`) now raises `YelpAPI.YelpAPIError` instead of a `TypeError` or `KeyError`.
|
|
11
|
+
* Path parameters (business IDs, aliases, event IDs, job aliases, and transaction types) are now percent-encoded, so characters like `/`, `?`, and `#` can no longer change the URL that is requested.
|
|
12
|
+
* Clarified the `YelpAPI.YelpAPIError` docs: HTTP error statuses raise `requests.HTTPError`, while `YelpAPIError` covers error objects and non-object bodies in successful responses.
|
|
13
|
+
* Updated docstrings to match Yelp's current docs (plan and access requirements, `match_threshold`, the 20-ID limit for `business_ids`, and required fields for Request a Quote).
|
|
14
|
+
* Fixed the examples so they catch `requests.HTTPError` and keep going when an endpoint needs a paid plan or extra access.
|
|
15
|
+
* Strengthened the test suite: every endpoint's URL, HTTP method, and parameters are now checked, and test data is deterministic.
|
|
16
|
+
* Added [Ruff](https://docs.astral.sh/ruff/) linting, run in CI.
|
|
17
|
+
|