Show ads from your account in your own Android app, video/audio player, or any standards-based ad integration — no WebView, no iframe. Uses the same slot keys as your website embed code. Android SDK below (§1–10); VAST/DAAST/Native for any other player at §11.
The Android SDK talks to a JSON version of the same ad-serving endpoint your website embed uses (serve.php / click.php). It renders the returned ad with native Android views — TextView, ImageView, or a Media3 player for audio/video — styled to match the colors and font you picked when creating the site/placement.
minSdk 24 or higher<uses-permission android:name="android.permission.INTERNET"/> in your app's manifest — easy to forget, and ads will silently fail to load without itThe SDK currently ships as source — a Gradle module you include directly in your project, not yet a published package.
adsdk folder.app module.settings.gradle.kts:
build.gradle.kts:
The zip also includes a short README.md with the same steps plus the quick-start code below, in case you're setting this up without this page open.
Initialize once, e.g. in your Application class:
Add an AdView to your layout:
Load an ad:
Log in to see your real slot keys here. Everything above this section works the same whether you're logged in or not — this is just a shortcut so you don't have to copy keys from My Sites by hand.
| Ad type | Rendered as |
|---|---|
| text | Styled headline + body text |
| image | Image with optional headline/body text |
| audio | Media3 (ExoPlayer) audio player with title/CTA |
| video | Media3 (ExoPlayer) video surface, autoplay muted with tap-to-unmute |
Every video ad is server-side transcoded to one consistent resolution/frame-rate/bitrate (admin-configured) before it can air, and capped at 30 seconds — so a player showing several video ads back to back (e.g. a VAST pod) never sees a resolution, frame-rate, or audio jump between them.
A placement's ad format (text/image, video, or audio) is fixed per slot — the same choice you made under My Sites — so a given AdView only ever loads compatible ads.
Background, text, accent, and border colors plus corner radius come from your slot's style settings automatically — no extra code needed. Two things don't map perfectly to native Android and are documented limitations, not bugs:
Teaser display style — if a site's display style is set to "Teaser" under My Sites, AdView continuously animates the whole ad unit the same way the web embed does, picking the animation from the slot's teaser animation setting:
| Teaser animation | Native behavior |
|---|---|
| pulse | Gently scales the ad up and down (1.6s cycle) |
| glow | A pulsing colored border overlay (1.8s cycle) — Android has no direct equivalent of a CSS box-shadow glow, so this is a deliberate visual approximation, same fixed color the web version uses |
| shake | A small side-to-side shake (2.4s cycle) |
A "Standard" display style (the default) has no animation. This applies to every ad type — text, image, audio, and video all animate the same way when teaser is on, matching the web embed's behavior of animating the whole ad container regardless of what's inside it.
When a user taps an ad, the SDK fires the click-tracking request in the background (never blocking or delaying anything) and immediately opens the destination URL in a Chrome Custom Tab, falling back to a normal browser intent if Custom Tabs isn't available. A slow or dropped tracking ping never affects what the user sees.
If a slot has auto-refresh enabled, AdView automatically loads a new ad on that interval while it's attached to the window, and stops when it's detached (e.g. the screen is left).
Any failure — network error, no eligible ad, malformed response — is handled the same way: AdView hides itself (visibility = GONE) and calls onAdListener.onAdFailedToLoad(reason). It never throws or crashes your app.
For video and audio you don't need the Android SDK at all — any player that speaks the industry-standard ad templates can pull ads directly. Text/image already is a native ad format, via the same JSON API below.
Video — VAST 4.0
Works with Google IMA SDK (web/Android/iOS), ExoPlayer's IMA extension, Video.js + videojs-contrib-ads, JW Player, and any other VAST 4.0-compliant player. Only slots with ad format "Video" return a real ad — anything else, or nothing eligible, returns a spec-valid empty <VAST/> document, never an error.
Ad pods: a request returns every currently eligible video ad for that slot, one per <Ad> element with an incrementing sequence, up to max (default 4, hard-capped at 10 regardless of what you pass — protects both your player and this server from an oversized response). Fewer eligible ads than the cap is fine, that's just a smaller pod; house ads fill any remaining slots the same way they fill a single-ad request. Each <Ad> is its own real impression with its own credit, exactly as if the player had made that many separate requests — a genuine 3-ad pod shown to one viewer is 3 real impressions, not the same one 3 times. Most players play a pod back-to-back as one ad break; if yours only understands a single ad, pass max=1 to get exactly one <Ad>.
Audio — DAAST 1.0
The IAB's audio-ad counterpart to VAST — same idea, <DAAST> root, <AudioClicks> instead of <VideoClicks>, same pod/max behavior described above. Works with podcast/streaming ad-insertion tools and any DAAST-aware player. Only slots with ad format "Audio" return a real ad.
Text & image — already a native ad format
No separate endpoint needed — the JSON from /api/serve.php (§12 below) already returns exactly what the IAB's Native Ad concept describes: a headline, body text, an image URL, and a destination link, as plain JSON instead of XML. Any native-ad rendering integration can consume it directly.
Scope, honestly
<Impression> pixel is included because compliant players expect one, but nothing depends on it actually firing.<MediaFile> width/height always reflect the real, current admin-configured target resolution — every video ad is transcoded to exactly that size, never a guess.For reference, or if you're integrating from something other than the Android SDK. Requests are unauthenticated aside from the slot key itself, matching the website embed.
GET https://adserver.unlimitedtextads.com/api/serve.php?slot={key}&pid={optional}&app_id={optional}
slot — required, your 32-character slot keypid — optional correlation id used to cap credited impressions per logical page/screen (the SDK generates one automatically per AdView)app_id — optional, your app's package name, used only as a fallback if pid is absentPOST https://adserver.unlimitedtextads.com/api/click.php?imp={impression_token}
Always responds {"status":"ok"} (HTTP 200), even for an unknown or expired token — fire this in the background and open the ad's destination_url immediately, don't wait for the response.
API traffic goes through the same fraud checks as the website embed (bot filtering, rate limiting, duplicate-impression detection, self-traffic detection). A flagged impression or click still renders/redirects normally — it's simply not credited.