AppsinityAPPSINITY
APIs

Analytics API

Analytics on Appsinity is event rows plus one aggregate read. The counters you see on a listing are recomputed from the event table on demand and written back onto your listing document.

Open Appsinity Studio

Reading your numbers

  • The listing identifier may be sent as listingId, appId or gameId; all three are accepted.
  • collection must be exactly apps to read the apps side; anything else resolves to games.
  • Reading this endpoint has a side effect: it recomputes downloads, visits, ratingAvg, ratingCount and reviewCount onto the listing document.
  • Sources are grouped and ordered by count, limited to twenty rows; country breakdowns to fifty.
GET /api/listing-analytics?collection=games&listingId=my-game
Authorization: Bearer <studio token>

200 {
  "ok": true,
  "downloads": 412,
  "visits": 9080,
  "reviewsSummary": { "ratingAvg": 4.3, "ratingCount": 27 },
  "sources":   [ { "source": "search", "count": 3110 }, ... ],   // top 20
  "countries": [ { "country": "IN", "event_type": "visit", "count": 2400 }, ... ]  // top 50
}

400 { "error": "Missing listing id" }
500 { "error": "Could not load listing analytics" }
Note: There is no date range parameter and no time series in the response. Everything is a lifetime aggregate, which is why Listing traffic and analytics shows totals rather than charts.

Recording events

CallBodyEffect
POST /api/listing-trafficcollection, listingId, eventType, optional source and countryOne listing_events row
POST /api/listing-playcollection, listingId, playSecondsOne web_game_sessions row
GET /api/listing-play-statsquery identifier onlySession totals for a listing
GET /api/listing-trafficpublic readThe counter a listing page prints
POST /api/listing-traffic
{ "collection": "games", "listingId": "my-game",
  "eventType": "visit", "source": "search", "country": "IN" }

// eventType is normalised: only the literal "visit" counts as a visit;
// every other value is recorded as "download"

POST /api/listing-play
{ "collection": "games", "listingId": "my-game", "playSeconds": 340 }
// playSeconds is clamped at 0 and rounded; 0 makes the call a no-op that still answers ok

GET /api/listing-play-stats?collection=games&listingId=my-game
200 { total_sessions, avg_play_seconds, total_play_seconds }
Watch out: These endpoints are not authenticated in a way that protects the count. Anyone can post a visit, and a token, if supplied, is only used to attach a uid. Treat every number as unaudited traffic.

Ratings and reviews

  • Rating is clamped to the range 1 to 5, so a submitted 9 becomes 5 and a 0 is refused as missing.
  • Text is capped at 2,000 characters, deviceId at 200 and source at 120.
  • One row per (collection, listing, uid): a second submission updates the first rather than adding to the average.
  • The author name shown is resolved from the store profile, falling back to the token's display name, then the email, then "Appsinity user".
  • The read is limited to the hundred most recently updated rows; the average covers all of them.
GET /api/listing-reviews?collection=games&listingId=my-game
200 {
  "ok": true,
  "reviews": [ { id, uid, userName, rating, text, createdAt, updatedAt } ],  // last 100
  "summary": { "ratingAvg": 4.3, "ratingCount": 27 }
}

POST /api/listing-reviews        Authorization: Bearer <player token>
{ "collection": "games", "listingId": "my-game", "rating": 4,
  "text": "...", "deviceId": "...", "source": "store" }

200 the stored row
400 { "error": "Missing listing id" } | "Choose a rating first"
401 { "error": "Sign in to write a review" }
404 { "error": "Listing not found" }
Not available: There is no moderation endpoint, no developer reply field and no report button in this API. Removal happens manually against the record, see Review and report moderation.

Public reads

Anything a player can see is available without a credential, and these are the calls worth building against.

CallAuthReturns
GET /api/catalog?type=game&limit=100Noneitems and games, published and visible rows only
GET /api/listings/:idNoneOne listing document
GET /api/listing-traffic?...NoneThe listing's counter
GET /api/developers/:idNoneThe public developer page
GET /api/streams/scheduledNoneUpcoming streams
GET /api/docs/:collectionOwner tokenYour own documents, newest first
Note: The catalogue predicate is a status of published without a pre-registration flag, so a draft, an unpublished or a pre-register row is absent from the read entirely, see Store visibility and status.

Ad events

Ad measurement lives on the same service and follows the same shape: one unit row plus a counter event per interaction.

POST /api/ads/:id/impression
POST /api/ads/:id/click
POST /api/ads/:id/completed
POST /api/ads/:id/skipped
PATCH /api/ads/:id/rate           // eCPM in cents, 0 .. 100000
GET  /api/ads/active
GET  /api/ads/stats
Note: Rates and events for the platform's own surfaces. There is no per-creative reporting and no callback, see Ads and monetisation.

Frequently asked questions

Can I get analytics per day?

No. The response is a lifetime aggregate: totals, top twenty sources and top fifty country-and-type pairs. There is no date parameter and no time series.

Is there an export?

No CSV or JSON export endpoint exists in Studio. Calling this endpoint yourself and storing the response is the only route.

Why did my download count change when I only opened the page?

Because reading analytics recomputes and writes the counters back onto the listing document. The number is derived from event rows, and the read refreshes the stored copy.

Do ad and play numbers reach the same table?

No. Traffic in listing_events, play sessions in web_game_sessions, ratings in listing_reviews, ad counters on the ad unit rows.

Need help?

Developer publishing support: [email protected]
General support: [email protected]
Partnerships: [email protected]