Install
openclaw skills install @scavio-ai/scavio-google-playSearch Google Play, read a full Android app listing including the real install count and Data safety table, and page reviews by cursor. 3 endpoints, 2 credits each, structured JSON.
openclaw skills install @scavio-ai/scavio-google-playSearch Google Play, pull a full Android store listing - including the real install count Play publishes but never renders, the whole permission tree and the Data safety table - and page reviews by cursor. All three endpoints return structured JSON.
Use this skill when the user asks to:
Get a free API key at https://scavio.dev (50 free credits to get started, no card required):
export SCAVIO_API_KEY=sk_live_your_key
Every request is a POST with a JSON body and:
Authorization: Bearer $SCAVIO_API_KEY
Base URL: https://api.scavio.dev. All paths are under /api/v1/googleplay. Every endpoint costs 2 credits.
| Endpoint | Credits | What it returns |
|---|---|---|
POST /api/v1/googleplay/search | 2 | One shelf of ranked apps (~30). No pagination. |
POST /api/v1/googleplay/app | 2 | The complete store listing, plus the 20 server-rendered reviews |
POST /api/v1/googleplay/reviews | 2 | A page of reviews, cursor-paginated |
Google Play is a premium domain upstream, which is why it is 2 credits and not 1. Budget accordingly before planning a deep review crawl.
/googleplay/search with query. You get one shelf of roughly 30 apps. A branded query also returns Play's hero card as result 1, projected to the same row shape, plus Play's related-query rail./googleplay/app with app_id - a package name (com.notion.id) or any play.google.com URL carrying one in its id param./googleplay/reviews when you need to page past those 20 or sort differently./googleplay/reviews with app_id, then send next_cursor back as cursor.Search does not paginate. It is one shelf of about 30 apps. There is no page or cursor parameter - do not invent one. Narrow the query instead.
/app does not paginate.
Reviews paginate by cursor, and the cursor is strict. It is opaque, single-use, and it encodes the sort as well as the position. Send it back with the same sort it came from. A cursor past the last review is a 404, not an empty page - that is the stop signal.
/search)| Parameter | Type | Default | Description |
|---|---|---|---|
query | string | required | Search term (1-200 chars) |
hl | string | en | Interface language (2-20 chars). Changes the storefront, not only the strings. |
gl | string | us | Country (2-10 chars) |
/app)| Parameter | Type | Default | Description |
|---|---|---|---|
app_id | string | required | Package name, or any play.google.com URL carrying one in its id param (1-500 chars) |
hl | string | en | Interface language |
gl | string | us | Country |
/reviews)| Parameter | Type | Default | Description |
|---|---|---|---|
app_id | string | required | Package name or Play URL (1-500 chars) |
sort | string | newest | relevance, newest, rating |
count | integer | 50 | Reviews per page, 1-200. Capped at 200 on our side. |
cursor | string | -- | The previous response's next_cursor. Opaque, single-use, sort-encoded. |
hl | string | en | Interface language |
gl | string | us | Country |
hl moves more than the wordsAt hl=pt-BR the title, the description, the install formatting and the content rating all change with it - it selects a storefront, not a translation layer. Play also silently falls back to English/US on any value it does not serve, so an unexpected language in the response means the value was not supported, not that the call failed.
import os, requests
BASE = "https://api.scavio.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['SCAVIO_API_KEY']}"}
# 1. Search - one shelf of ~30 apps, no pagination
apps = requests.post(f"{BASE}/api/v1/googleplay/search", headers=HEADERS,
json={"query": "habit tracker", "hl": "en", "gl": "us"}).json()
# 2. Full listing - package name or any play.google.com URL
app = requests.post(f"{BASE}/api/v1/googleplay/app", headers=HEADERS,
json={"app_id": "com.spotify.music"}).json()
# The 20 server-rendered reviews are already in there - do not pay again for them.
# 3. Page past them, sorted by rating
reviews = requests.post(f"{BASE}/api/v1/googleplay/reviews", headers=HEADERS,
json={"app_id": "com.spotify.music", "sort": "rating", "count": 200}).json()
Cursor paging, capped so it cannot run away with the user's credits. The cursor
encodes the sort, so the sort must not change between pages, and running past the
end is a 404:
def paged_reviews(app_id, sort="newest", count=200, max_pages=5):
"""2 credits per page. 5 pages = 10 credits."""
cursor, pages = None, []
for _ in range(max_pages):
body = {"app_id": app_id, "sort": sort, "count": count}
if cursor:
body["cursor"] = cursor # same sort every time: the cursor encodes it
r = requests.post(f"{BASE}/api/v1/googleplay/reviews", headers=HEADERS, json=body)
if r.status_code == 404:
break # cursor ran past the last review: this is the end
data = r.json()["data"]
pages.append(data)
cursor = data.get("next_cursor")
if not cursor:
break
return pages
Every response uses the envelope { data, response_time, credits_used, credits_remaining }.
next_cursor./app already contains 20 reviews. Do not call /reviews for the same app unless you need to go deeper or change the sort - that is a second premium call for data you already have.sort fixed while paging reviews. The cursor carries the sort, and changing it mid-walk invalidates the sequence.404 mid-walk as a failure - a cursor past the last review is how the feed ends.400 means an invalid or missing parameter - fix and retry.401 means the API key is invalid or missing. Check SCAVIO_API_KEY.404 on /reviews while paging means the cursor ran past the last review. Stop; this is normal./app before crawling reviews for it.429 means rate or usage limit exceeded. Wait before retrying. See https://scavio.dev/docs/rate-limits.502 / 503 mean upstream is temporarily unavailable - wait a few seconds and retry.SCAVIO_API_KEY is not set, prompt the user to export it before continuing.langchain-scavio has no Google Play tool - use the Scavio SDK directly:
pip install scavio
from scavio import ScavioClient
client = ScavioClient() # reads SCAVIO_API_KEY
apps = client.google_play.search("habit tracker", gl="us")
app = client.google_play.app("com.spotify.music")
page1 = client.google_play.reviews("com.spotify.music", sort="rating", count=200)
page2 = client.google_play.reviews("com.spotify.music", sort="rating", count=200,
cursor=page1["data"]["next_cursor"])
JavaScript / TypeScript:
npm install scavio
import { Scavio } from "scavio";
const scavio = new Scavio(); // reads SCAVIO_API_KEY
const app = await scavio.googlePlay.app({ app_id: "com.spotify.music" });