Add image upload support (/image API) for Google Lens - #38
Conversation
There was a problem hiding this comment.
Pull request overview
Adds first-class image upload support to serpapi-python for Google Lens workflows via SerpApi’s /image POST endpoint, enabling users to upload local images and then perform Lens searches using the returned image_id.
Changes:
- Added
Client.upload_image(image, **kwargs)(and module-levelserpapi.upload_image) to upload images as multipart form data. - Updated HTTP API-key injection logic to avoid injecting into query params when
api_keyis already provided in form data. - Added unit tests and documentation/examples covering URL-based Lens search and upload-then-search workflows.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
tests/test_image_upload.py |
Adds unit coverage for multipart upload behavior, input types, and Google Lens URL vs image_id usage. |
serpapi/http.py |
Adjusts API-key injection to support endpoints that authenticate via form data. |
serpapi/core.py |
Implements Client.upload_image(...) and exposes module-level upload_image. |
README.md |
Documents Google Lens usage via URL and via uploaded image_id. |
docs/index.rst |
Exposes upload_image in Sphinx API reference for both module function and Client method. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
tanysheng
left a comment
There was a problem hiding this comment.
Thank you @adarshdigievo 👍, mostly looks good!
| def test_google_lens_search_supports_url_and_uploaded_image_id(): | ||
| client = serpapi.Client(api_key="test-api-key") | ||
| client.session.request = Mock( | ||
| side_effect=[ | ||
| json_response(b'{"image_id": "uploaded-image-123"}'), | ||
| json_response(b'{"search_metadata": {"status": "Success"}}'), | ||
| json_response(b'{"search_metadata": {"status": "Success"}}'), | ||
| ] | ||
| ) | ||
|
|
||
| upload = client.upload_image(BytesIO(b"fake-image-data")) | ||
| client.search(engine="google_lens", image_id=upload["image_id"]) | ||
| client.search(engine="google_lens", url="https://example.com/image.png") | ||
|
|
||
| upload_search = client.session.request.call_args_list[1][1]["params"] | ||
| url_search = client.session.request.call_args_list[2][1]["params"] | ||
| assert upload_search["image_id"] == "uploaded-image-123" | ||
| assert "url" not in upload_search | ||
| assert url_search["url"] == "https://example.com/image.png" | ||
| assert "image_id" not in url_search |
There was a problem hiding this comment.
Don't think this tests much. It just asserts the params we passed in, which is self-fulfilling. I think we can remove it.
There was a problem hiding this comment.
@tanysheng Thanks for the review. I removed the test.
Summary
This PR adds image-upload support for Google Lens, using the new
/imagePOST endpoint.Changes
serpapi.upload_image(image, **kwargs).POST /imageusing:imageas the file field.api_keyand any additional supplied arguments as form data.os.PathLike/pathlib.PathBytesIOThis means users can pass an image path or an open image binary file to
upload_image()client.search(engine="google_lens", url=...)upload_image(...)followed byclient.search(..., image_id=...)Live Tests
Image uploads were verified using the live tests below:
results = client.search(engine="google_lens", url="https://raw.githubusercontent.com/serpapi/serpapi-python/master/docs/_static/serpapi-python.png")6a756f57ad9ecfd6f1ff2edcupload = client.upload_image(IMAGE_PATH); results = client.search(engine="google_lens", image_id=upload["image_id"])6a756f5a55cfe1c665a6b616pathlib.Pathimage = Path(IMAGE_PATH); upload = client.upload_image(image); results = client.search(engine="google_lens", image_id=upload["image_id"])6a756f5d4c437f4d4996fb65with open(IMAGE_PATH, "rb") as image: upload = client.upload_image(image); results = client.search(engine="google_lens", image_id=upload["image_id"])6a756f60f0114a9bff17f8d0BytesIOimage = BytesIO(Path(IMAGE_PATH).read_bytes()); upload = client.upload_image(image); results = client.search(engine="google_lens", image_id=upload["image_id"])6a756f63bb48ad0e678bc1b7