
Typed, coroutine-friendly SerpApi client offering strongly-typed engine responses (search, images, jobs, finance, YouTube, profiles), raw JSON fallback, and a flexible open-parameter DSL.
An idiomatic, coroutine-based Kotlin SDK for SerpApi, built with Ktor Client
and kotlinx.serialization. It is implemented as a Kotlin Multiplatform library with a JVM target
for backend applications.
The SDK does not wrap SerpApi's Java client and does not depend on Gson. Shared API and networking
logic live in commonMain, while the JVM client uses Ktor's CIO engine.
This project is under active development. The current JVM artifact targets Java 11 bytecode and can be used by Ktor, Spring Boot, command-line tools, workers, and other JVM applications.
The generic API can call any engine represented by SerpApiEngine and return raw JSON. Typed response
models are added engine by engine as their schemas are reviewed and tested.
| Engine | API | Typed schema coverage | Typed response details |
|---|---|---|---|
google |
googleSearch(...) |
~25% | Common Google blocks including metadata, organic results, related questions/searches, pagination, AI overview, perspectives, and embedded jobs. Other blocks remain available through raw JSON. |
google_light |
googleLightSearch(...) |
~95% | Organic results and sitelinks, related questions/searches, top stories, latest articles, search state, and offset pagination. Variable knowledge graph and answer-box fields remain flexible JSON. |
google_jobs |
googleJobsSearch(...) |
~95% | Job results, descriptions, highlights, application options, filters, pagination, and detected extensions. Deprecated and highly variable chips data remains flexible JSON. |
google_jobs_listing |
googleJobsListing(...) |
100% | Company ratings currently exposed by SerpApi for an individual job ID. |
google_finance |
googleFinanceSearch(...) |
~90% | Markets, graph points, summary, knowledge graph, news, financial statements, key events, suggestions, and discovery groups. Highly variable futures-chain and top-news data remains flexible JSON. |
google_finance_markets |
googleFinanceMarkets(...) |
~95% | Regional indexes, currencies, cryptocurrency, futures, price movements, and market news. Variable top-news content remains flexible JSON. |
google_images |
googleImagesSearch(...) |
~95% | Full-resolution image results, dimensions, sources, product flags, related content IDs, suggested/related searches, shopping results, and page batching. |
google_images_light |
googleImagesLightSearch(...) |
100% | Lightweight image results, licensing fields, product flags, search state, and offset-based pagination. |
facebook_profile |
facebookProfile(...) |
100% | Page, creator, general, and private-profile fields including contact details, links, photos, work, education, and about sections. |
instagram_profile |
instagramProfile(...) |
~98% | Profile/business metadata, bio links, posts, media resources, video/reel fields, carousels, locations, tagged users, and token pagination. Undocumented fact-check and gating objects remain flexible JSON. |
youtube |
youtubeSearch(...) |
~95% | Videos, ads, movies, playlists, channels, Shorts, related searches, thumbnails, and continuation-token pagination. |
youtube_video |
youtubeVideo(...) |
~95% | Video and channel metadata, description links, chapters, products, comments, replies, sorting/pagination tokens, and transcript links. |
youtube_video_transcript |
youtubeVideoTranscript(...) |
100% | Timed transcript snippets, chapters, language and transcript-track details, selection state, and links to alternative tracks. |
Coverage percentages estimate how much of each engine's currently documented response schema has a dedicated typed SDK model. They do not represent endpoint uptime, request success rate, or support for every possible future field. Raw JSON remains available for all unmodeled data.
| Endpoint | SDK API | Result |
|---|---|---|
| Search JSON | search(...) |
Raw JsonObject, typed SDK model, or caller-owned serializable model |
| Search HTML | search(..., SearchOutput.Html) |
Raw HTML String
|
| Search Markdown | search(..., SearchOutput.Markdown) |
LLM-friendly Markdown String
|
| Locations | locations(...) |
JsonArray |
| Search Archive | searchArchive(...) |
JsonObject |
| Account | account(...) |
JsonObject |
serpapi/
src/commonMain/ Shared SDK implementation and response models
src/commonTest/ Shared request and deserialization tests
src/jvmMain/ JVM client factory using Ktor CIO
server/ Example Ktor backend consuming the SDK
The configured Maven Central coordinates are:
dependencies {
implementation("io.github.stevdza-san:serpapi-kotlin:0.1.0")
}Keep API keys outside source control and reuse one client for the lifetime of your application:
import com.stevdza.serpapi.sdk.client.createJvmSerpApi
import com.stevdza.serpapi.sdk.config.SerpApiConfig
val serpApi = createJvmSerpApi(
config = SerpApiConfig(
apiKey = System.getenv("SERPAPI_KEY"),
),
)The production base URL is used by default. A different URL can be supplied for integration tests:
val config = SerpApiConfig(
apiKey = "test-key",
baseUrl = "http://localhost:9090",
timeoutMillis = 5_000,
)Call serpApi.close() during application shutdown to release the underlying HTTP resources.
Use googleSearch for typed access to commonly used Google response blocks:
import com.stevdza.serpapi.sdk.client.googleSearch
import com.stevdza.serpapi.sdk.model.SerpApiCountry
import com.stevdza.serpapi.sdk.model.SerpApiLanguage
val response = serpApi.googleSearch {
query = "Kotlin coroutines"
location = "Austin, Texas"
language = SerpApiLanguage.ENGLISH
country = SerpApiCountry.UNITED_STATES
"num" to 10
}
response.organicResults.forEach { result ->
println("${result.title}: ${result.link}")
}
response.aiOverview?.references?.forEach { reference ->
println("AI source: ${reference.title} — ${reference.link}")
}Known common parameters are typed. Engine-specific parameters remain available through the open parameter DSL:
val response = serpApi.googleSearch {
query = "Kotlin"
"device" to "mobile"
"safe" to "active"
}Search the dedicated Google Jobs engine with typed results:
import com.stevdza.serpapi.sdk.client.googleJobsSearch
val jobs = serpApi.googleJobsSearch {
query = "Kotlin backend"
location = "Belgrade, Serbia"
}
jobs.jobsResults.forEach { job ->
println("${job.title} at ${job.companyName} — ${job.location}")
println(job.detectedExtensions?.scheduleType)
job.applyOptions.forEach { option ->
println("Apply through ${option.title}: ${option.link}")
}
}Known detected extensions such as salary, schedule, remote work, insurance, and paid time off are
typed. New fields introduced by SerpApi are retained in additionalProperties.
Use a returned job ID to retrieve the company ratings currently provided by the Jobs Listing engine:
import com.stevdza.serpapi.sdk.client.googleJobsListing
val jobId = jobs.jobsResults.firstNotNullOf { it.jobId }
val listing = serpApi.googleJobsListing(jobId)
listing.ratings.forEach { rating ->
println("${rating.source}: ${rating.rating} (${rating.reviews} reviews)")
}Compact jobs embedded in a regular Google response are available through
googleSearch(...).jobsResults.
Retrieve typed quote, graph, market, company, financial, and news data from the dedicated Google Finance engine:
import com.stevdza.serpapi.sdk.client.GoogleFinanceWindow
import com.stevdza.serpapi.sdk.client.googleFinanceSearch
import com.stevdza.serpapi.sdk.model.SerpApiLanguage
val finance = serpApi.googleFinanceSearch(
query = "GOOGL:NASDAQ",
language = SerpApiLanguage.ENGLISH,
window = GoogleFinanceWindow.ONE_YEAR,
)
println("${finance.summary?.title}: ${finance.summary?.price}")
println("Movement: ${finance.summary?.priceMovement?.percentage}%")
finance.financials.forEach { statement ->
println(statement.title)
statement.results.forEach { period ->
println("${period.date} (${period.periodType})")
}
}Supported graph windows are represented by GoogleFinanceWindow: 1D, 5D, 1M, 6M, YTD,
1Y, 5Y, and MAX. Suggestions are returned when Google cannot resolve a query directly.
The separate Google Finance Markets engine provides a typed market overview:
import com.stevdza.serpapi.sdk.client.googleFinanceMarkets
import com.stevdza.serpapi.sdk.model.SerpApiCountry
import com.stevdza.serpapi.sdk.model.SerpApiLanguage
val markets = serpApi.googleFinanceMarkets(
language = SerpApiLanguage.ENGLISH,
country = SerpApiCountry.UNITED_STATES,
)
markets.markets?.us?.forEach { asset ->
println("${asset.name}: ${asset.price}")
}
markets.newsResults.forEach { article ->
println("${article.source}: ${article.link}")
}SerpApi currently accepts only the indexes trend for this engine, represented by
GoogleFinanceMarketTrend.INDEXES. Google currently provides a reduced amount of data through this
page, so returned market lists may be smaller than historical API examples.
Use Google Light when you need the critical Google result blocks with lower response times than the full Google engine:
import com.stevdza.serpapi.sdk.client.google.googleLightSearch
import com.stevdza.serpapi.sdk.model.SerpApiCountry
import com.stevdza.serpapi.sdk.model.SerpApiLanguage
val results = serpApi.googleLightSearch {
query = "Kotlin backend"
location = "Belgrade, Serbia"
language = SerpApiLanguage.ENGLISH
country = SerpApiCountry.SERBIA
"start" to 0
}
results.organicResults.forEach { result ->
println("${result.title}: ${result.link}")
}Increase start by 10 for the next page. The typed response includes organic results, sitelinks,
related questions and searches, top stories, latest articles, and pagination. Knowledge graph and
answer-box payloads remain JsonElement because their fields vary by query.
Use the full Google Images engine for rich image metadata and batches of roughly 100 results:
import com.stevdza.serpapi.sdk.client.GoogleImagesDevice
import com.stevdza.serpapi.sdk.client.googleImagesSearch
import com.stevdza.serpapi.sdk.model.SerpApiCountry
import com.stevdza.serpapi.sdk.model.SerpApiLanguage
val images = serpApi.googleImagesSearch(
query = "Kotlin logo",
page = 0,
language = SerpApiLanguage.ENGLISH,
country = SerpApiCountry.UNITED_STATES,
device = GoogleImagesDevice.DESKTOP,
)
images.imagesResults.forEach { image ->
println("${image.title}: ${image.original}")
println("${image.originalWidth} × ${image.originalHeight}")
}Increment page to request the next image batch. Suggested searches, related searches, shopping
results, product availability, and related-content IDs are included in the typed response.
The Images Light engine provides a smaller response and uses a numeric result offset:
import com.stevdza.serpapi.sdk.client.googleImagesLightSearch
val lightImages = serpApi.googleImagesLightSearch(
query = "Kotlin logo",
start = 0,
language = SerpApiLanguage.ENGLISH,
country = SerpApiCountry.UNITED_STATES,
)
val nextPage = lightImages.serpApiPagination?.nextThe Light API accepts offsets from 0 through 999. Both image APIs expose original URLs,
dimensions, source pages, thumbnails, licensing information when available, and safety/product flags.
Search YouTube with typed result variants and continue through result pages using SerpApi's token:
import com.stevdza.serpapi.sdk.client.youtubeSearch
val results = serpApi.youtubeSearch(query = "Kotlin coroutines")
results.videoResults.forEach { video ->
println("${video.title} — ${video.channel?.name}: ${video.link}")
}
val nextPage = results.serpApiPagination?.nextPageToken?.let { token ->
serpApi.youtubeSearch("Kotlin coroutines", continuationToken = token)
}Fetch the details, chapters, products, and first comments page for an individual video:
import com.stevdza.serpapi.sdk.client.youtubeVideo
val video = serpApi.youtubeVideo(videoId = "F9UC9DY-vIU")
println("${video.title}: ${video.views}")
video.chapters.forEach { chapter ->
println("${chapter.timeStart}s — ${chapter.title}")
}
val nextComments = video.commentsNextPageToken?.let { token ->
serpApi.youtubeVideo("F9UC9DY-vIU", nextPageToken = token)
}The same nextPageToken parameter accepts comment-reply tokens returned by an individual comment.
Sorting tokens and transcript SerpApi links are exposed by the typed response. Less common or newly
introduced request parameters can be passed through extraParameters.
Retrieve the transcript itself using the video ID and, optionally, a language or transcript type:
import com.stevdza.serpapi.sdk.client.youtubeVideoTranscript
val transcript = serpApi.youtubeVideoTranscript(
videoId = "Gk8gB5VACZw",
languageCode = "en",
type = "asr",
)
transcript.transcript.forEach { entry ->
println("${entry.startTimeText}: ${entry.snippet}")
}
transcript.availableTranscripts.forEach { track ->
println("${track.languageName} (${track.languageCode}) — ${track.title ?: track.type}")
}Use title to select a named transcript track. Language codes are strings because this endpoint
supports extended codes such as es-ES and zh-Hans, in addition to two-letter codes.
Fetch a Facebook page, creator, general, or private profile by its URL identifier:
import com.stevdza.serpapi.sdk.client.facebook.facebookProfile
val facebook = serpApi.facebookProfile(profileId = "modernatx")
val facebookProfile = facebook.profileResults
println("${facebookProfile?.name}: ${facebookProfile?.followers}")Fetch an Instagram profile and its posts, then use its token to request the next post page:
import com.stevdza.serpapi.sdk.client.instagram.instagramProfile
val instagram = serpApi.instagramProfile(profileId = "serpapicom")
instagram.profileResults?.posts?.forEach { post ->
println("${post.shortcode}: ${post.likedByCount} likes")
}
val nextPage = instagram.serpApiPagination?.nextPageToken?.let { token ->
serpApi.instagramProfile(
profileId = "serpapicom",
nextPageToken = token,
)
}Both APIs require the profile identifier from the social profile URL, rather than the complete URL. Only publicly available profile data returned by SerpApi is represented.
Raw JSON remains available for engines or response blocks that do not yet have SDK models:
import com.stevdza.serpapi.sdk.model.SerpApiEngine
val raw = serpApi.search(SerpApiEngine.GOOGLE_IMAGES) {
query = "Kotlin logo"
}Callers can deserialize a response into their own @Serializable model:
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerialName
import kotlinx.serialization.json.JsonObject
@Serializable
data class MyResponse(
@SerialName("organic_results")
val organicResults: List<JsonObject> = emptyList(),
)
val response = serpApi.search<MyResponse>(
engine = SerpApiEngine.GOOGLE,
parameters = mapOf("q" to "Kotlin"),
)val locations = serpApi.locations(query = "Austin", limit = 5)
val archivedSearch = serpApi.searchArchive(searchId = "search-id")
val account = serpApi.account()HTML output is also supported:
import com.stevdza.serpapi.sdk.model.SearchOutput
val html = serpApi.search(
engine = SerpApiEngine.GOOGLE,
output = SearchOutput.Html,
) {
query = "Kotlin"
}Markdown output is optimized for LLMs and agents, using compact tables, links, and YAML frontmatter:
val markdown = serpApi.search(
engine = SerpApiEngine.GOOGLE_LIGHT,
output = SearchOutput.Markdown,
) {
query = "Kotlin backend"
}This uses SerpApi's /search.md endpoint and returns the Markdown response unchanged as a String.
HTTP failures, SerpApi error responses, network errors, and invalid JSON are reported as
SerpApiFailure. Its statusCode and responseBody fields are available when provided by the
server. Coroutine cancellation is propagated unchanged. Invalid local arguments throw
IllegalArgumentException.
The server module demonstrates using the SDK from a backend. Define SERPAPI_KEY in the process
environment and run:
./gradlew :server:runThe server listens on port 8080 by default and currently exposes:
GET /health
GET /search?q=Kotlin
GET /jobs?q=Kotlin%20backend&location=Belgrade%2C%20Serbia
./gradlew :serpapi:jvmTest :serpapi:jvmJar :server:buildAn idiomatic, coroutine-based Kotlin SDK for SerpApi, built with Ktor Client
and kotlinx.serialization. It is implemented as a Kotlin Multiplatform library with a JVM target
for backend applications.
The SDK does not wrap SerpApi's Java client and does not depend on Gson. Shared API and networking
logic live in commonMain, while the JVM client uses Ktor's CIO engine.
This project is under active development. The current JVM artifact targets Java 11 bytecode and can be used by Ktor, Spring Boot, command-line tools, workers, and other JVM applications.
The generic API can call any engine represented by SerpApiEngine and return raw JSON. Typed response
models are added engine by engine as their schemas are reviewed and tested.
| Engine | API | Typed schema coverage | Typed response details |
|---|---|---|---|
google |
googleSearch(...) |
~25% | Common Google blocks including metadata, organic results, related questions/searches, pagination, AI overview, perspectives, and embedded jobs. Other blocks remain available through raw JSON. |
google_light |
googleLightSearch(...) |
~95% | Organic results and sitelinks, related questions/searches, top stories, latest articles, search state, and offset pagination. Variable knowledge graph and answer-box fields remain flexible JSON. |
google_jobs |
googleJobsSearch(...) |
~95% | Job results, descriptions, highlights, application options, filters, pagination, and detected extensions. Deprecated and highly variable chips data remains flexible JSON. |
google_jobs_listing |
googleJobsListing(...) |
100% | Company ratings currently exposed by SerpApi for an individual job ID. |
google_finance |
googleFinanceSearch(...) |
~90% | Markets, graph points, summary, knowledge graph, news, financial statements, key events, suggestions, and discovery groups. Highly variable futures-chain and top-news data remains flexible JSON. |
google_finance_markets |
googleFinanceMarkets(...) |
~95% | Regional indexes, currencies, cryptocurrency, futures, price movements, and market news. Variable top-news content remains flexible JSON. |
google_images |
googleImagesSearch(...) |
~95% | Full-resolution image results, dimensions, sources, product flags, related content IDs, suggested/related searches, shopping results, and page batching. |
google_images_light |
googleImagesLightSearch(...) |
100% | Lightweight image results, licensing fields, product flags, search state, and offset-based pagination. |
facebook_profile |
facebookProfile(...) |
100% | Page, creator, general, and private-profile fields including contact details, links, photos, work, education, and about sections. |
instagram_profile |
instagramProfile(...) |
~98% | Profile/business metadata, bio links, posts, media resources, video/reel fields, carousels, locations, tagged users, and token pagination. Undocumented fact-check and gating objects remain flexible JSON. |
youtube |
youtubeSearch(...) |
~95% | Videos, ads, movies, playlists, channels, Shorts, related searches, thumbnails, and continuation-token pagination. |
youtube_video |
youtubeVideo(...) |
~95% | Video and channel metadata, description links, chapters, products, comments, replies, sorting/pagination tokens, and transcript links. |
youtube_video_transcript |
youtubeVideoTranscript(...) |
100% | Timed transcript snippets, chapters, language and transcript-track details, selection state, and links to alternative tracks. |
Coverage percentages estimate how much of each engine's currently documented response schema has a dedicated typed SDK model. They do not represent endpoint uptime, request success rate, or support for every possible future field. Raw JSON remains available for all unmodeled data.
| Endpoint | SDK API | Result |
|---|---|---|
| Search JSON | search(...) |
Raw JsonObject, typed SDK model, or caller-owned serializable model |
| Search HTML | search(..., SearchOutput.Html) |
Raw HTML String
|
| Search Markdown | search(..., SearchOutput.Markdown) |
LLM-friendly Markdown String
|
| Locations | locations(...) |
JsonArray |
| Search Archive | searchArchive(...) |
JsonObject |
| Account | account(...) |
JsonObject |
serpapi/
src/commonMain/ Shared SDK implementation and response models
src/commonTest/ Shared request and deserialization tests
src/jvmMain/ JVM client factory using Ktor CIO
server/ Example Ktor backend consuming the SDK
The configured Maven Central coordinates are:
dependencies {
implementation("io.github.stevdza-san:serpapi-kotlin:0.1.0")
}Keep API keys outside source control and reuse one client for the lifetime of your application:
import com.stevdza.serpapi.sdk.client.createJvmSerpApi
import com.stevdza.serpapi.sdk.config.SerpApiConfig
val serpApi = createJvmSerpApi(
config = SerpApiConfig(
apiKey = System.getenv("SERPAPI_KEY"),
),
)The production base URL is used by default. A different URL can be supplied for integration tests:
val config = SerpApiConfig(
apiKey = "test-key",
baseUrl = "http://localhost:9090",
timeoutMillis = 5_000,
)Call serpApi.close() during application shutdown to release the underlying HTTP resources.
Use googleSearch for typed access to commonly used Google response blocks:
import com.stevdza.serpapi.sdk.client.googleSearch
import com.stevdza.serpapi.sdk.model.SerpApiCountry
import com.stevdza.serpapi.sdk.model.SerpApiLanguage
val response = serpApi.googleSearch {
query = "Kotlin coroutines"
location = "Austin, Texas"
language = SerpApiLanguage.ENGLISH
country = SerpApiCountry.UNITED_STATES
"num" to 10
}
response.organicResults.forEach { result ->
println("${result.title}: ${result.link}")
}
response.aiOverview?.references?.forEach { reference ->
println("AI source: ${reference.title} — ${reference.link}")
}Known common parameters are typed. Engine-specific parameters remain available through the open parameter DSL:
val response = serpApi.googleSearch {
query = "Kotlin"
"device" to "mobile"
"safe" to "active"
}Search the dedicated Google Jobs engine with typed results:
import com.stevdza.serpapi.sdk.client.googleJobsSearch
val jobs = serpApi.googleJobsSearch {
query = "Kotlin backend"
location = "Belgrade, Serbia"
}
jobs.jobsResults.forEach { job ->
println("${job.title} at ${job.companyName} — ${job.location}")
println(job.detectedExtensions?.scheduleType)
job.applyOptions.forEach { option ->
println("Apply through ${option.title}: ${option.link}")
}
}Known detected extensions such as salary, schedule, remote work, insurance, and paid time off are
typed. New fields introduced by SerpApi are retained in additionalProperties.
Use a returned job ID to retrieve the company ratings currently provided by the Jobs Listing engine:
import com.stevdza.serpapi.sdk.client.googleJobsListing
val jobId = jobs.jobsResults.firstNotNullOf { it.jobId }
val listing = serpApi.googleJobsListing(jobId)
listing.ratings.forEach { rating ->
println("${rating.source}: ${rating.rating} (${rating.reviews} reviews)")
}Compact jobs embedded in a regular Google response are available through
googleSearch(...).jobsResults.
Retrieve typed quote, graph, market, company, financial, and news data from the dedicated Google Finance engine:
import com.stevdza.serpapi.sdk.client.GoogleFinanceWindow
import com.stevdza.serpapi.sdk.client.googleFinanceSearch
import com.stevdza.serpapi.sdk.model.SerpApiLanguage
val finance = serpApi.googleFinanceSearch(
query = "GOOGL:NASDAQ",
language = SerpApiLanguage.ENGLISH,
window = GoogleFinanceWindow.ONE_YEAR,
)
println("${finance.summary?.title}: ${finance.summary?.price}")
println("Movement: ${finance.summary?.priceMovement?.percentage}%")
finance.financials.forEach { statement ->
println(statement.title)
statement.results.forEach { period ->
println("${period.date} (${period.periodType})")
}
}Supported graph windows are represented by GoogleFinanceWindow: 1D, 5D, 1M, 6M, YTD,
1Y, 5Y, and MAX. Suggestions are returned when Google cannot resolve a query directly.
The separate Google Finance Markets engine provides a typed market overview:
import com.stevdza.serpapi.sdk.client.googleFinanceMarkets
import com.stevdza.serpapi.sdk.model.SerpApiCountry
import com.stevdza.serpapi.sdk.model.SerpApiLanguage
val markets = serpApi.googleFinanceMarkets(
language = SerpApiLanguage.ENGLISH,
country = SerpApiCountry.UNITED_STATES,
)
markets.markets?.us?.forEach { asset ->
println("${asset.name}: ${asset.price}")
}
markets.newsResults.forEach { article ->
println("${article.source}: ${article.link}")
}SerpApi currently accepts only the indexes trend for this engine, represented by
GoogleFinanceMarketTrend.INDEXES. Google currently provides a reduced amount of data through this
page, so returned market lists may be smaller than historical API examples.
Use Google Light when you need the critical Google result blocks with lower response times than the full Google engine:
import com.stevdza.serpapi.sdk.client.google.googleLightSearch
import com.stevdza.serpapi.sdk.model.SerpApiCountry
import com.stevdza.serpapi.sdk.model.SerpApiLanguage
val results = serpApi.googleLightSearch {
query = "Kotlin backend"
location = "Belgrade, Serbia"
language = SerpApiLanguage.ENGLISH
country = SerpApiCountry.SERBIA
"start" to 0
}
results.organicResults.forEach { result ->
println("${result.title}: ${result.link}")
}Increase start by 10 for the next page. The typed response includes organic results, sitelinks,
related questions and searches, top stories, latest articles, and pagination. Knowledge graph and
answer-box payloads remain JsonElement because their fields vary by query.
Use the full Google Images engine for rich image metadata and batches of roughly 100 results:
import com.stevdza.serpapi.sdk.client.GoogleImagesDevice
import com.stevdza.serpapi.sdk.client.googleImagesSearch
import com.stevdza.serpapi.sdk.model.SerpApiCountry
import com.stevdza.serpapi.sdk.model.SerpApiLanguage
val images = serpApi.googleImagesSearch(
query = "Kotlin logo",
page = 0,
language = SerpApiLanguage.ENGLISH,
country = SerpApiCountry.UNITED_STATES,
device = GoogleImagesDevice.DESKTOP,
)
images.imagesResults.forEach { image ->
println("${image.title}: ${image.original}")
println("${image.originalWidth} × ${image.originalHeight}")
}Increment page to request the next image batch. Suggested searches, related searches, shopping
results, product availability, and related-content IDs are included in the typed response.
The Images Light engine provides a smaller response and uses a numeric result offset:
import com.stevdza.serpapi.sdk.client.googleImagesLightSearch
val lightImages = serpApi.googleImagesLightSearch(
query = "Kotlin logo",
start = 0,
language = SerpApiLanguage.ENGLISH,
country = SerpApiCountry.UNITED_STATES,
)
val nextPage = lightImages.serpApiPagination?.nextThe Light API accepts offsets from 0 through 999. Both image APIs expose original URLs,
dimensions, source pages, thumbnails, licensing information when available, and safety/product flags.
Search YouTube with typed result variants and continue through result pages using SerpApi's token:
import com.stevdza.serpapi.sdk.client.youtubeSearch
val results = serpApi.youtubeSearch(query = "Kotlin coroutines")
results.videoResults.forEach { video ->
println("${video.title} — ${video.channel?.name}: ${video.link}")
}
val nextPage = results.serpApiPagination?.nextPageToken?.let { token ->
serpApi.youtubeSearch("Kotlin coroutines", continuationToken = token)
}Fetch the details, chapters, products, and first comments page for an individual video:
import com.stevdza.serpapi.sdk.client.youtubeVideo
val video = serpApi.youtubeVideo(videoId = "F9UC9DY-vIU")
println("${video.title}: ${video.views}")
video.chapters.forEach { chapter ->
println("${chapter.timeStart}s — ${chapter.title}")
}
val nextComments = video.commentsNextPageToken?.let { token ->
serpApi.youtubeVideo("F9UC9DY-vIU", nextPageToken = token)
}The same nextPageToken parameter accepts comment-reply tokens returned by an individual comment.
Sorting tokens and transcript SerpApi links are exposed by the typed response. Less common or newly
introduced request parameters can be passed through extraParameters.
Retrieve the transcript itself using the video ID and, optionally, a language or transcript type:
import com.stevdza.serpapi.sdk.client.youtubeVideoTranscript
val transcript = serpApi.youtubeVideoTranscript(
videoId = "Gk8gB5VACZw",
languageCode = "en",
type = "asr",
)
transcript.transcript.forEach { entry ->
println("${entry.startTimeText}: ${entry.snippet}")
}
transcript.availableTranscripts.forEach { track ->
println("${track.languageName} (${track.languageCode}) — ${track.title ?: track.type}")
}Use title to select a named transcript track. Language codes are strings because this endpoint
supports extended codes such as es-ES and zh-Hans, in addition to two-letter codes.
Fetch a Facebook page, creator, general, or private profile by its URL identifier:
import com.stevdza.serpapi.sdk.client.facebook.facebookProfile
val facebook = serpApi.facebookProfile(profileId = "modernatx")
val facebookProfile = facebook.profileResults
println("${facebookProfile?.name}: ${facebookProfile?.followers}")Fetch an Instagram profile and its posts, then use its token to request the next post page:
import com.stevdza.serpapi.sdk.client.instagram.instagramProfile
val instagram = serpApi.instagramProfile(profileId = "serpapicom")
instagram.profileResults?.posts?.forEach { post ->
println("${post.shortcode}: ${post.likedByCount} likes")
}
val nextPage = instagram.serpApiPagination?.nextPageToken?.let { token ->
serpApi.instagramProfile(
profileId = "serpapicom",
nextPageToken = token,
)
}Both APIs require the profile identifier from the social profile URL, rather than the complete URL. Only publicly available profile data returned by SerpApi is represented.
Raw JSON remains available for engines or response blocks that do not yet have SDK models:
import com.stevdza.serpapi.sdk.model.SerpApiEngine
val raw = serpApi.search(SerpApiEngine.GOOGLE_IMAGES) {
query = "Kotlin logo"
}Callers can deserialize a response into their own @Serializable model:
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerialName
import kotlinx.serialization.json.JsonObject
@Serializable
data class MyResponse(
@SerialName("organic_results")
val organicResults: List<JsonObject> = emptyList(),
)
val response = serpApi.search<MyResponse>(
engine = SerpApiEngine.GOOGLE,
parameters = mapOf("q" to "Kotlin"),
)val locations = serpApi.locations(query = "Austin", limit = 5)
val archivedSearch = serpApi.searchArchive(searchId = "search-id")
val account = serpApi.account()HTML output is also supported:
import com.stevdza.serpapi.sdk.model.SearchOutput
val html = serpApi.search(
engine = SerpApiEngine.GOOGLE,
output = SearchOutput.Html,
) {
query = "Kotlin"
}Markdown output is optimized for LLMs and agents, using compact tables, links, and YAML frontmatter:
val markdown = serpApi.search(
engine = SerpApiEngine.GOOGLE_LIGHT,
output = SearchOutput.Markdown,
) {
query = "Kotlin backend"
}This uses SerpApi's /search.md endpoint and returns the Markdown response unchanged as a String.
HTTP failures, SerpApi error responses, network errors, and invalid JSON are reported as
SerpApiFailure. Its statusCode and responseBody fields are available when provided by the
server. Coroutine cancellation is propagated unchanged. Invalid local arguments throw
IllegalArgumentException.
The server module demonstrates using the SDK from a backend. Define SERPAPI_KEY in the process
environment and run:
./gradlew :server:runThe server listens on port 8080 by default and currently exposes:
GET /health
GET /search?q=Kotlin
GET /jobs?q=Kotlin%20backend&location=Belgrade%2C%20Serbia
./gradlew :serpapi:jvmTest :serpapi:jvmJar :server:build