Skip to content

Commit 9694090

Browse files
author
Anonymous Committer
committed
Add high-level SDK client and OpenAPI release control plane
1 parent 30c6657 commit 9694090

48 files changed

Lines changed: 2997 additions & 584 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/generate.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: generate-sdks
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches: [main]
7+
paths:
8+
- "config/**"
9+
- "openapi/**"
10+
- "overlays/**"
11+
- "scripts/**"
12+
13+
jobs:
14+
generate:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v4
18+
- uses: actions/setup-python@v5
19+
with:
20+
python-version: "3.11"
21+
- uses: actions/setup-java@v4
22+
with:
23+
distribution: temurin
24+
java-version: "21"
25+
- name: Install orchestration dependencies
26+
run: python -m pip install --upgrade pip
27+
- name: Fetch canonical spec when it is not committed
28+
if: ${{ hashFiles('openapi/justserpapi.openapi.json') == '' && secrets.JUSTSERPAPI_API_KEY != '' }}
29+
env:
30+
JUSTSERPAPI_API_KEY: ${{ secrets.JUSTSERPAPI_API_KEY }}
31+
run: python scripts/sdkctl.py fetch-spec
32+
- name: Validate examples and canonical spec when available
33+
run: |
34+
python scripts/sdkctl.py validate-examples
35+
if [ ! -f openapi/justserpapi.openapi.json ]; then
36+
echo "Canonical spec not available; generation skipped."
37+
exit 0
38+
fi
39+
python scripts/sdkctl.py validate-spec
40+
python scripts/sdkctl.py generate --clean
41+
- name: Upload generation artifacts
42+
if: ${{ hashFiles('openapi/justserpapi.openapi.json') != '' }}
43+
uses: actions/upload-artifact@v4
44+
with:
45+
name: generated-sdks
46+
path: |
47+
.generated
48+
openapi/justserpapi.openapi.json

.github/workflows/python.yml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,26 @@ permissions:
1212

1313
jobs:
1414
build:
15-
1615
runs-on: ubuntu-latest
1716
strategy:
1817
matrix:
19-
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
18+
python-version: ["3.9", "3.10", "3.11", "3.12"]
2019

2120
steps:
2221
- uses: actions/checkout@v4
2322
- name: Set up Python ${{ matrix.python-version }}
24-
uses: actions/setup-python@v4
23+
uses: actions/setup-python@v5
2524
with:
2625
python-version: ${{ matrix.python-version }}
2726
- name: Install dependencies
2827
run: |
2928
python -m pip install --upgrade pip
30-
pip install -r requirements.txt
3129
pip install -r test-requirements.txt
32-
- name: Test with pytest
30+
pip install -e .
31+
- name: Validate docs and tests
3332
run: |
33+
python scripts/sdkctl.py validate-examples
3434
pytest --cov=justserpapi
35+
- name: Build distribution artifacts
36+
run: |
37+
python -m build

.github/workflows/release.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: release-sdks
2+
3+
on:
4+
workflow_dispatch:
5+
6+
jobs:
7+
sync-and-push:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v4
11+
- uses: actions/setup-python@v5
12+
with:
13+
python-version: "3.11"
14+
- uses: actions/setup-java@v4
15+
with:
16+
distribution: temurin
17+
java-version: "21"
18+
- name: Install orchestration dependencies
19+
run: python -m pip install --upgrade pip
20+
- name: Fetch canonical spec when it is not committed
21+
if: ${{ hashFiles('openapi/justserpapi.openapi.json') == '' }}
22+
env:
23+
JUSTSERPAPI_API_KEY: ${{ secrets.JUSTSERPAPI_API_KEY }}
24+
run: python scripts/sdkctl.py fetch-spec
25+
- name: Require canonical spec
26+
run: test -f openapi/justserpapi.openapi.json
27+
- name: Generate release artifacts
28+
run: |
29+
python scripts/sdkctl.py validate-examples
30+
python scripts/sdkctl.py validate-spec
31+
python scripts/sdkctl.py generate --clean
32+
- name: Sync SDK repositories
33+
env:
34+
SDK_REPO_PUSH_TOKEN: ${{ secrets.SDK_REPO_PUSH_TOKEN }}
35+
run: python scripts/sdkctl.py sync-repos --push --tag

.github/workflows/validate.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: validate-control-plane
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
validate:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
- uses: actions/setup-python@v5
14+
with:
15+
python-version: "3.11"
16+
- uses: actions/setup-java@v4
17+
with:
18+
distribution: temurin
19+
java-version: "21"
20+
- name: Install test dependencies
21+
run: |
22+
python -m pip install --upgrade pip
23+
pip install -r test-requirements.txt
24+
pip install -e .
25+
- name: Run orchestration tests
26+
run: python -m unittest discover -s tests -v
27+
- name: Validate documentation examples
28+
run: python scripts/sdkctl.py validate-examples
29+
- name: Validate fixture spec
30+
run: python scripts/sdkctl.py validate-spec --spec tests/fixtures/sample-openapi.json --skip-generator-validate
31+
- name: Fetch canonical spec when it is not committed
32+
if: ${{ hashFiles('openapi/justserpapi.openapi.json') == '' && secrets.JUSTSERPAPI_API_KEY != '' }}
33+
env:
34+
JUSTSERPAPI_API_KEY: ${{ secrets.JUSTSERPAPI_API_KEY }}
35+
run: python scripts/sdkctl.py fetch-spec
36+
- name: Validate canonical spec when present
37+
run: |
38+
if [ -f openapi/justserpapi.openapi.json ]; then
39+
python scripts/sdkctl.py validate-spec
40+
else
41+
echo "Canonical spec not available; skipping."
42+
fi
43+
- name: Run breaking-change check when baseline exists
44+
run: |
45+
if [ -f openapi/justserpapi.openapi.json ] && [ -f openapi/baseline/justserpapi.openapi.json ]; then
46+
python scripts/sdkctl.py breaking-check
47+
else
48+
echo "Baseline or head spec missing; skipping."
49+
fi

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ target/
6666
# IDEs
6767
.idea/
6868
.vscode/
69+
.DS_Store
6970

7071
# Project specific excludes
7172
.output.txt
@@ -78,3 +79,6 @@ git_push.sh
7879
temp-openapi-google.json
7980
check_responses.py
8081
restore_files.py
82+
.generated/
83+
.repositories/
84+
.cache/openapi-generator/

README.md

Lines changed: 61 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -19,110 +19,93 @@
1919
</a>
2020
</p>
2121

22-
<p align="center">
23-
<strong>The most reliable and high-performance SERP API for Google Search, Maps, News, Shopping, and more.</strong>
24-
</p>
25-
26-
---
22+
OpenAPI-driven Python SDK for JustSerpAPI with a stable high-level `Client` as the public entrypoint.
2723

28-
## 🌐 Quick Links
24+
## Installation
2925

30-
- **Official Website**: [justserpapi.com](https://justserpapi.com)
31-
- **API Documentation**: [docs.justserpapi.com](https://docs.justserpapi.com)
32-
- **User Dashboard**: [dashboard.justserpapi.com](https://dashboard.justserpapi.com)
33-
- **Support**: [support@justserpapi.com](mailto:support@justserpapi.com)
26+
```bash
27+
pip install justserpapi
28+
```
3429

35-
---
30+
## Quick Start
3631

37-
## 🚀 Overview
32+
```python
33+
from justserpapi import Client
34+
35+
with Client(api_key="YOUR_API_KEY") as client:
36+
response = client.google.search(
37+
query="coffee shops in New York",
38+
location="New York, NY",
39+
language="en",
40+
)
41+
print(response.organic_results)
42+
```
3843

39-
JustSerpAPI provides a comprehensive suite of tools to scrape and parse search engine results in real-time. This Python SDK allows you to integrate Google Search, Maps, Images, News, Shopping, and AI-powered overviews directly into your Python applications with full type safety and high performance.
44+
## Promoted High-Level API
4045

41-
### Key Features
42-
- **Full Google Coverage**: Search, Maps, News, Shopping, Finance, Images, and Patents.
43-
- **AI-Powered**: Access Google AI Overviews and AI Search modes.
44-
- **Easy Integration**: Built-in authentication, automatic retries, and clean models.
45-
- **Type Safety**: Full PEP 484 type hints support for a great developer experience.
46+
The high-level surface is designed to be the default entrypoint:
4647

47-
---
48+
```python
49+
from justserpapi import Client
4850

49-
## 🛠 Installation
51+
client = Client(api_key="YOUR_API_KEY", timeout=20.0)
5052

51-
Install the package via `pip`:
53+
search = client.google.search(query="best espresso beans", language="en")
54+
maps = client.google.maps.search(query="espresso bars", location="Shanghai")
55+
news = client.google.news.search(query="OpenAI", language="en")
56+
images = client.google.images.search(query="espresso machine")
57+
shopping = client.google.shopping.search(query="espresso tamper")
58+
overview = client.google.ai.overview(url="https://example.com/ai-overview")
5259

53-
```bash
54-
pip install justserpapi
60+
client.close()
5561
```
5662

57-
---
63+
Promoted high-level responses are parsed into Pydantic models:
5864

59-
## 📖 Getting Started
65+
- `GoogleSearchResponse`
66+
- `GoogleMapsSearchResponse`
67+
- `GoogleNewsSearchResponse`
68+
- `GoogleImagesSearchResponse`
69+
- `GoogleShoppingSearchResponse`
70+
- `GoogleAIOverviewResponse`
6071

61-
To start using the SDK, you need an **API Key**. You can get one from the [User Dashboard](https://dashboard.justserpapi.com).
72+
## Configuration
6273

63-
### Simple Search Example
74+
The public client exposes the common knobs directly:
6475

6576
```python
66-
import justserpapi
67-
from justserpapi.api.google_api_api import GoogleAPIApi
68-
from pprint import pprint
69-
70-
# Configure the SDK
71-
configuration = justserpapi.Configuration(
72-
host="https://api.justserpapi.com"
77+
from justserpapi import Client
78+
from urllib3.util.retry import Retry
79+
80+
client = Client(
81+
api_key="YOUR_API_KEY",
82+
base_url="https://api.justserpapi.com",
83+
timeout=(5.0, 30.0),
84+
retries=Retry(total=5, backoff_factor=0.5),
7385
)
74-
configuration.api_key['ApiKeyAuth'] = 'YOUR_API_KEY_HERE'
75-
76-
# Initialize the API client
77-
with justserpapi.ApiClient(configuration) as api_client:
78-
# Create an instance of the Google API
79-
api_instance = GoogleAPIApi(api_client)
80-
81-
try:
82-
# Search for "coffee shops in New York"
83-
response = api_instance.search(
84-
query="coffee shops in New York",
85-
location="New York,NY",
86-
language="en"
87-
)
88-
pprint(response)
89-
except justserpapi.ApiException as e:
90-
print(f"Exception when calling API: {e}")
86+
client.close()
9187
```
9288

93-
---
94-
95-
## 🍱 Supported APIs
96-
97-
| Service | Method | Description |
98-
| :--- | :--- | :--- |
99-
| **Google Search** | `search()` | Core Google search results (Organic, Ads, etc.) |
100-
| **Google Maps** | `maps_search()` | Local business listings and place details |
101-
| **Google AI** | `ai_overview()` | Extract AI-generated summaries from Google |
102-
| **Google Images** | `images_search()` | High-quality image search results |
103-
| **Google News** | `news_search()` | Real-time news results |
104-
| **Google Shopping**| `shopping_search()`| Comprehensive product and price data |
89+
- `api_key`: value sent in the `X-API-Key` header
90+
- `base_url`: API host, defaults to `https://api.justserpapi.com`
91+
- `timeout`: default request timeout injected into promoted high-level methods
92+
- `retries`: `urllib3` retry configuration; defaults to a conservative retry strategy for the high-level client
10593

106-
Check out the [Full Documentation](https://docs.justserpapi.com) for a complete list of endpoints and parameters.
94+
## OpenAPI Control Plane
10795

108-
---
96+
This repository is driven by the canonical OpenAPI document plus the SDK control-plane files in `config/`, `scripts/`, and `overlays/`.
10997

110-
## 🛡️ Authentication
98+
- If `openapi/justserpapi.openapi.json` is committed, local generation is fully reproducible.
99+
- If it is not committed, CI can fetch and cache it by running `python scripts/sdkctl.py fetch-spec` with `JUSTSERPAPI_API_KEY` configured.
111100

112-
The API uses an API Key passed in the `X-API-Key` header. In the SDK, this is managed via the `Configuration` object:
101+
Typical maintenance flow:
113102

114-
```python
115-
configuration.api_key['ApiKeyAuth'] = 'YOUR_API_KEY'
103+
```bash
104+
python scripts/sdkctl.py validate-examples
105+
python scripts/sdkctl.py validate-spec --skip-generator-validate
106+
python scripts/sdkctl.py generate --clean
116107
```
117108

118-
---
119-
120-
## ⚖️ License
109+
## License
121110

122111
Distributed under the MIT License. See `LICENSE` for more information.
123-
124-
---
125-
126-
<p align="center">
127-
Proudly maintained by the <a href="https://justserpapi.com">JustSerpAPI Team</a>.
128-
</p>

config/generators/go.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"packageName": "{{PACKAGE_NAME}}",
3+
"moduleName": "{{MODULE_NAME}}",
4+
"packageVersion": "{{SPEC_VERSION}}",
5+
"hideGenerationTimestamp": true,
6+
"enumClassPrefix": true,
7+
"generateInterfaces": true
8+
}

config/generators/java.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"groupId": "{{GROUP_ID}}",
3+
"artifactId": "{{ARTIFACT_ID}}",
4+
"artifactVersion": "{{SPEC_VERSION}}",
5+
"artifactDescription": "{{SERVICE_DISPLAY_NAME}} Java SDK generated from the canonical OpenAPI document.",
6+
"invokerPackage": "{{INVOKER_PACKAGE}}",
7+
"apiPackage": "{{API_PACKAGE}}",
8+
"modelPackage": "{{MODEL_PACKAGE}}",
9+
"library": "okhttp-gson",
10+
"dateLibrary": "java8",
11+
"hideGenerationTimestamp": true,
12+
"developerName": "{{SERVICE_DISPLAY_NAME}}",
13+
"developerEmail": "support@justserpapi.com",
14+
"scmConnection": "scm:git:{{REPO_REMOTE}}",
15+
"scmDeveloperConnection": "scm:git:{{REPO_REMOTE}}",
16+
"scmUrl": "{{REPO_REMOTE}}"
17+
}

config/generators/python.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"packageName": "{{PACKAGE_NAME}}",
3+
"projectName": "{{REPO_NAME}}",
4+
"packageVersion": "{{SPEC_VERSION}}",
5+
"packageUrl": "{{REPO_REMOTE}}",
6+
"httpUserAgent": "{{PACKAGE_NAME}}/{{SPEC_VERSION}}/python",
7+
"library": "urllib3",
8+
"hideGenerationTimestamp": true,
9+
"generateSourceCodeOnly": false
10+
}

0 commit comments

Comments
 (0)