Skip to content

Agent research API

Mosaic’s v1 product surface is six tools that AI agents call directly (ADR 0026). Each tool is one REST operation in the operation catalog, and its MCP tool is generated from the same entry, so inputs, outputs, and error codes are identical on both transports. The default MCP catalog at https://api.mosaic.dev/mcp is these six tools, and its tools/list advertises only the implemented ones: today all six. An unimplemented tool would not be listed or callable, so an agent is never offered a tool that fails on every call.

Tool REST Status
get_meta_company_ads POST /v1/meta/company-ads Implemented locally; not deployed
get_research_result GET /v1/research-requests/{requestId} Implemented locally; not deployed
retrieve_brand POST /v1/brands/retrieve Implemented locally; paid fresh work once configured
find_creator_socials POST /v1/creators/socials Implemented locally, Mosaic’s own signals (unpriced)
search_youtube_sponsorships POST /v1/youtube/sponsorships Implemented locally; paid fresh work once configured
get_usage GET /v1/usage Implemented locally; not deployed

retrieve_brand and search_youtube_sponsorships run fresh paid work exactly like get_meta_company_ads once the deployment publishes their tariff, which is a configuration change (proposed: 10 and 5 Work Credits). Until then, and on any deployment that has not published them, they answer from data Mosaic already holds for free and refuse fresh work with tariff_unpublished (see Unpublished tariffs). find_creator_socials answers free from Mosaic’s own signals; its external provider discovery has no tariff. Authentication, the error envelope, Request-Id, and rate limits follow the API reference. Monitoring operations listed there are frozen: their data and REST routes remain, and they are not in the default MCP catalog. get_work_credit_account and get_workspace_billing are no longer MCP tools; get_usage replaces them, and their REST operations (GET /v1/work-credits, GET /v1/billing) remain.

agent ── get_meta_company_ads(pageId, maxAgeMs, maxCredits, key) ──▶ Mosaic
◀── succeeded (cache hit, 0 credits) → read ads, page with get_research_result
◀── pending (fresh work, credits held) → poll get_research_result(requestId)
◀── resolution_required (candidate Pages) → call again with the chosen pageId

There is no quote step. A call either answers from cache for free, starts fresh work under a fixed tariff, or asks the agent to choose a Page.

The four acquisition tools (get_meta_company_ads, retrieve_brand, find_creator_socials, search_youtube_sponsorships) share three inputs.

Input Rule
maxAgeMs Oldest acceptable source observation, in milliseconds, measured from when Mosaic observed the source. 0 requires fresh work. Each tool sets its own default and maximum.
maxCredits The most Work Credits this call may hold and charge. Defaults to the tool’s published tariff, or 0 while it is unpublished. 0 means cache-only: a cache hit succeeds and paid work is never dispatched. Fresh work needs maxCredits at least the tariff.
idempotencyKey Required. REST sends it as the Idempotency-Key header. Repeating a call with the same key and inputs returns the original answer and never charges twice; the same key with different inputs is 409 conflict.

A replay sets the Idempotent-Replayed: true header in REST and _meta["dev.mosaic/idempotent-replayed"] in MCP. Page-size inputs such as limit are not part of the replay identity.

  • Cache hit. When a completed observation of the same source and scope is no older than maxAgeMs, the call answers synchronously with cacheHit: true and creditsCharged: 0. Observations from any Workspace’s research count; the requesting Workspace is never revealed to others.
  • Fresh work. Otherwise Mosaic holds the tariff before any provider work starts and answers status: "pending" with a requestId and pollAfterMs. Meta runs measured 7–166 seconds.
  • Settlement. Bounded work that completes, including a valid empty result, settles the hold: creditsCharged equals the tariff. Failed or interrupted work releases the hold and answers status: "failed" with creditsCharged: 0. A provider start whose outcome is unknown keeps the hold for at most 24 hours from dispatch, then releases it without charge.
  • Unpublished tariff. A tool whose tariff is not published yet answers cache hits as usual and refuses fresh work with 409 tariff_unpublished, before any hold or record. get_usage lists the tariff as unpublished. Like the spending refusals, it is a business state, not an outage: repeating the same call cannot succeed.
  • Polling, pagination, and replays are free. get_research_result never charges.
Field Meaning
status succeeded, pending, failed, or resolution_required
observedAt When Mosaic observed the source for this answer; null until it succeeds
coverage The bounded scope searched, whether it completed or hit a cap, and truncated
classifierVersion The pinned classifier that produced each classification
cacheHit true when the answer reused an earlier observation
creditsCharged Credits settled for this call: 0 for cache hits, pending work, failures, and resolution
creditsHeld Credits currently held for pending work

An empty or failed result is never proof of absence. coverage states what was searched; failed means Mosaic could not observe the source.

POST /v1/meta/company-ads · MCP get_meta_company_ads · write scope · tariff 25 Work Credits per fresh call

Returns the active US Meta Ads Library ads of one advertiser Page, each classified from Meta’s explicit branded-content attribution.

Field Type Notes
pageId numeric string A Meta Page ID. Exactly one of pageId, companyName, domain.
companyName string, 1–200 Resolved against canonical Brand names and Page names Mosaic has observed. Case-insensitive exact match; no fuzzy matching.
domain hostname Resolved against canonical Brands’ primary company domains. Social, hosting, and link-in-bio hosts are rejected.
maxAgeMs integer, 0–604 800 000 Default 259 200 000 (72 hours). Maximum 7 days.
maxCredits integer, 0–1 000 000 Default 25, the current tariff. 0 is cache-only. Fresh work needs at least 25.
limit integer, 1–100 Ads on the first page. Default 25.
idempotencyKey string, 1–255 Required. REST header Idempotency-Key.

A pageId is researched directly. A companyName or domain is resolved first, using only data Mosaic already holds:

Outcome When Answer
exact Exactly one active canonical Brand matches, and it has exactly one effective confirmed Meta Page Research that Page
ambiguous Several Brands or Pages match, or the match is only an observed Page name status: "resolution_required" with candidate Pages
not_found Nothing matches status: "resolution_required" with no candidates and a message that the agent must supply pageId

Each candidate carries its basis: catalog_brand_confirmed_page (a staff- or onboarding-confirmed Page of a canonical Brand) or observed_page_name (a Page Mosaic has seen with that exact name, which proves nothing about ownership). Resolution never charges, holds, or records a request, and the idempotency key is not consumed. Call again with the chosen pageId. Mosaic has no Page search yet; for an unknown company, the agent must find the Page ID in the Meta Ad Library and pass pageId.

The first call for a Page creates a durable research subject: { id, source: "meta", pageId, pageName, pageUrl, canonicalBrandId }. canonicalBrandId is set only when the Page is an effective confirmed Meta target of exactly one active canonical Brand; otherwise it is null. Research never creates a Brand, Matcher, confirmed Source Target, Context.dev retrieval, tracking, or Event. Explicit partnerships of a linked subject also become global Relationship evidence; those of an unlinked subject stay in this answer and its receipt.

Each ad reports the parties exactly as Meta displayed them and one of three outcomes:

attribution basis Rule
explicit_partnership requested_page_displayed_partner_tagged The requested Page displayed the ad; a distinct Page is the branded-content party
explicit_partnership partner_displayed_requested_page_tagged A distinct Page displayed the ad; the requested Page is the branded-content party
brand_only no_branded_content_party The requested Page displayed the ad; Meta reported no branded-content party
brand_only requested_page_tagged_itself The requested Page displayed the ad and is also its own branded-content party
unknown display_page_missing The display Page is missing or unreadable
unknown branded_content_malformed The branded-content field is present but unreadable
unknown branded_content_state_unrecorded The row predates recording of the branded-content field’s state
unknown requested_page_not_a_party Neither the display nor the branded-content Page is the requested Page

brand_only means no explicit partnership was observed in Meta’s attribution fields, not that none exists. A missing or malformed field is always unknown. Classification uses only typed Page fields: ad copy, images, and links never decide it. partner is the other party of an explicit partnership; when the requested Page is a company, the partner is the creator. It carries creatorId when Mosaic’s catalog already links that Page to a Creator.

Brand-only ads are retained as minimal rows, so they carry identity, parties, and start date but no creative. Explicit partnerships also carry the ad’s text, title, and link. Ads hidden by suppression or erasure are omitted and counted in coverage.counts.hidden.

{
"data": {
"object": "research_result",
"id": "5b0c…",
"workspaceId": "1f3a…",
"kind": "meta_company_ads",
"status": "succeeded",
"cacheHit": true,
"creditsCharged": 0,
"creditsHeld": 0,
"maxCredits": 25,
"tariff": { "name": "meta-brand-research-v1", "credits": 25 },
"observedAt": "2026-10-13T18:02:11.000Z",
"classifierVersion": "meta-attribution-2026-10-14",
"pollAfterMs": null,
"resolution": {
"input": { "kind": "page_id", "value": "620732004665539" },
"outcome": "exact",
"candidates": [],
"message": null
},
"subject": {
"id": "0d2e…",
"source": "meta",
"pageId": "620732004665539",
"pageName": "Skylight",
"pageUrl": "https://www.facebook.com/620732004665539",
"canonicalBrandId": null
},
"coverage": {
"scope": { "country": "US", "activeStatus": "ACTIVE", "providerResultCap": 2000 },
"status": "complete",
"truncated": false,
"returnedAdCount": 97,
"counts": { "explicitPartnership": 12, "brandOnly": 84, "unknown": 1, "hidden": 0 }
},
"failure": null,
"createdAt": "2026-10-14T17:20:00.000Z",
"ads": {
"data": [
{
"archiveId": "1682112446211506",
"adLibraryUrl": "https://www.facebook.com/ads/library/?id=1682112446211506",
"startedAt": "2026-09-30T00:00:00.000Z",
"attribution": "explicit_partnership",
"basis": "partner_displayed_requested_page_tagged",
"requestedPageRole": "tagged",
"displayPage": { "pageId": "571154359564610", "name": "Target Does It Again" },
"taggedPage": { "pageId": "620732004665539", "name": "Skylight" },
"partner": {
"pageId": "571154359564610",
"name": "Target Does It Again",
"creatorId": null
},
"creative": { "text": "…", "title": null, "linkUrl": "https://…" }
}
],
"nextCursor": "eyJ2Ijox…"
}
}
}

Ads are ordered explicit partnerships first, then brand-only, then unknown, each by archive ID. ads is null unless status is succeeded. startedAt is the ad’s start date when Meta reported it; Meta shows US commercial ads only while active, so observedAt is when Mosaic saw the ad, not when a partnership began.

Every status answers 200. A pending answer carries id, creditsHeld: 25, and pollAfterMs. A resolution_required answer has id: null, subject: null, the candidates under resolution, and a resolution.message that tells the agent to choose a candidate or supply pageId.

Status Code When
400 invalid_input Not exactly one of pageId, companyName, domain; a malformed value; a social or hosting domain; maxAgeMs out of range
404 not_found The Page cannot be researched (for example, it is suppressed)
409 conflict The idempotency key was used with different inputs
409 credit_limit_below_quote Fresh work is needed and maxCredits is below the tariff (including 0, cache-only); spending cap max_credits
409 insufficient_credits Fresh work is needed and the Workspace cannot fund the tariff; spending cap work_credit_balance
409 billing_frozen New paid work is frozen pending billing review; spending cap billing_review. Cache hits still succeed
409 collection_in_progress Fresh work for this Page is already running. Retry after it finishes with maxAgeMs > 0 to reuse it free
429 rate_limited Too many requests
503 collection_disabled No cached result is fresh enough and fresh Meta collection is not enabled or has no tariff; cache hits still succeed

The three spending refusals carry the cap in error.details, with every value a string, and nothing was held or dispatched:

{
"error": {
"code": "insufficient_credits",
"status": 409,
"message": "This work needs 25 Work Credits; 4 are available. Nothing was charged or dispatched. Do not retry until a human buys a credit pack.",
"requestId": "…",
"details": {
"cap": "work_credit_balance",
"action": "buy_credit_pack",
"retryable": "false",
"requiredCredits": "25",
"availableCredits": "4"
}
}
}
details.cap details.action Other keys
work_credit_balance buy_credit_pack requiredCredits, availableCredits
max_credits raise_max_credits requiredCredits, maxCredits
billing_review contact_support requiredCredits
overage_cap (legacy subscriptions only) raise_overage_cap requiredCredits, availableCredits

retryable is always "false": none of these refusals is lifted by repeating the call. Over MCP the same details appear in the tool error with a stop hint.

A tool whose tariff get_usage reports as unpublished never holds, charges, or dispatches provider work. When an answer needs fresh work, the call is refused before anything is recorded, whatever maxCredits says, and the idempotency key is not consumed:

{
"error": {
"code": "tariff_unpublished",
"status": 409,
"message": "No cached Context.dev profile of this domain is within maxAgeMs, and fresh retrieval has no published tariff, so nothing was charged or dispatched. …",
"requestId": "…",
"details": {
"product": "brand_profile",
"tariff": "unpublished",
"action": "use_cached_result",
"retryable": "false"
}
}
}

product is the get_usage tariff product (brand_profile, creator_socials, or youtube_sponsorship_search). The refusal is 409 on every tool, like the spending refusals: nothing is held, and retryable is "false" because repeating the call cannot succeed until the price is published. Over MCP the same details appear in the tool error with one static stop hint. Cached results keep answering for free.

GET /v1/research-requests/{requestId} · MCP get_research_result · read scope · free

Returns the same research_result object for a request this Workspace created, whichever research tool created it, including its current status and one page of results. Its kind names the tool: meta_company_ads, brand_profile, creator_socials, or youtube_sponsorship_search. Inputs: requestId, cursor (from ads.nextCursor, candidateAccounts.nextCursor, or videos.nextCursor), and limit (1–100, default 25). A cursor is valid only for the same request. A request from another Workspace and a missing request both answer 404 not_found. A malformed cursor is 400 invalid_input.

A failed answer carries failure.reason: acquisition_failed (the provider run failed), coverage_incomplete (the run stopped before a bounded result), or start_unconfirmed (the provider start could not be confirmed within 24 hours).

POST /v1/brands/retrieve · MCP retrieve_brand · write scope · cached profiles free; fresh retrieval at the published tariff (proposed 10 Work Credits), unpublished by default

Returns a Context.dev brand profile for a company domain, with provenance on every value. It is enrichment: the profile never confirms an identity, and calling the tool never creates or changes a canonical Brand, its curated profile, a Matcher, or a Source Target (CONTEXT invariant 12).

Field Type Notes
domain hostname A company domain. Exactly one of domain, pageId. Social, hosting, and link-in-bio hosts are rejected.
pageId numeric string A Meta Page, such as get_meta_company_ads’ subject.pageId. It resolves only to the confirmed primary domain of the one canonical Brand it is an effective confirmed Meta target of.
maxAgeMs integer, 0–31 536 000 000 Default 7 776 000 000 (90 days). Maximum 365 days. Measured from Context.dev’s own observation of the values.
maxCredits integer, 0–1 000 000 Defaults to the published tariff, or 0 while it is unpublished. 0 is cache-only. Fresh retrieval needs at least the tariff.
idempotencyKey string, 1–255 Required. REST header Idempotency-Key.

A pageId that no exact identity match links to one canonical Brand with a primary domain answers status: "resolution_required" with resolution.outcome not_found (or ambiguous, listing each Brand’s domain), no charge, and a message to call again with domain. Mosaic never infers a domain from a Page or a name.

The common result fields, kind: "brand_profile", and:

  • subject: { id, source: "domain", domain, canonicalBrandId }. canonicalBrandId is set when the domain is the primary company domain of exactly one active canonical Brand.
  • profile: domain; identity (catalog_brand when canonicalBrandId is set, else unconfirmed; the values are Context.dev candidates either way); name and description (each a field or null); and industries, logos ({ role, url }), and socialLinks ({ network, url }), each a list of fields. A field is { value, provenance: { provider: "context.dev", snapshotId, retrievedAt, sourcePath } }.
  • coverage: { provider: "context.dev", lookup: "by_domain", status: "complete", truncated: false }.
  • observedAt and every retrievedAt: when Context.dev observed the values (its cache age applied to Mosaic’s retrieval time), not when Mosaic stored them. snapshotId names the retained snapshot or fresh-retrieval receipt the values came from.
  • profile and coverage are null while status is pending or failed.
  • Social links to a suppressed or erased Meta Page or YouTube Channel are omitted whenever the result is read, and reappear if a suppression is reversed. Accounts are recognized by their native ID (facebook.com/<Page ID>, youtube.com/channel/UC…); a vanity URL is not matched. Erasure also removes them from the stored receipt, including when the Page or Channel is erased while the retrieval is in flight; the retrieval is still charged.

Any retained successful Context.dev profile of the domain observed within maxAgeMs answers synchronously with cacheHit: true and creditsCharged: 0, whichever Workspace’s research, staff curation, or onboarding retrieved it. Polling and replays are free.

Otherwise, with a published tariff, the call holds the tariff and answers status: "pending" with creditsHeld and pollAfterMs; Mosaic’s worker then calls Context.dev with the same maxAgeMs (so Context.dev’s own cache cannot answer with an older profile; 0 forces a refresh), allowing it 60 seconds (cold retrievals measured a 14.5-second median, with some past 25 seconds), so poll get_research_result:

Context.dev outcome Result
A profile (Context.dev bills it, whether or not it served from its cache) succeeded; the hold settles: creditsCharged = tariff
400, 404, or 422 (no company), 408 (its timeout), 401, 429, or a malformed or partial envelope failed, failure.reason: "acquisition_failed", no charge
No answer: the connection failed, Mosaic’s wait ended, or a 5xx failed, failure.reason: "start_unconfirmed", no charge
Not sent: the deployment can no longer call Context.dev failed, failure.reason: "acquisition_failed", no charge

Context.dev has no idempotency key, so each fresh request is sent to Context.dev at most once: an unanswered request is never sent again and never charged. Call again with a new key to try once more. A fresh profile is retained as a research receipt that any Workspace can reuse as a cache hit; it is never applied to a canonical Brand’s profile. While another fresh retrieval of the same domain is running, the call is 409 collection_in_progress; retry after it with maxAgeMs > 0 for free.

Without a published tariff, the call is 409 tariff_unpublished: it holds, dispatches, and records nothing.

As get_meta_company_ads (including the spending refusals and collection_in_progress), plus 409 tariff_unpublished (above). 400 invalid_input covers a missing or doubled reference and social or hosting hosts.

POST /v1/creators/socials · MCP find_creator_socials · write scope · free from Mosaic’s own signals; provider discovery unpriced

Returns a creator’s public social accounts. Staff-confirmed accounts and candidate accounts are separate lists. Mosaic never merges identities from candidates (CONTEXT invariant 14), never promotes a candidate to confirmed, and never returns contact details such as emails, business inquiry links, websites, or management lines (ADR 0025).

Field Type Notes
creatorId UUID A canonical Creator. Exactly one of creatorId, pageId, channelId, url.
pageId numeric string A Meta Page, such as an explicit partnership’s partner.pageId from get_meta_company_ads.
channelId UC… string A canonical YouTube Channel ID.
url string, 1–255 A facebook.com Page URL, or a youtube.com/channel/UC… or youtube.com/@handle URL. A handle resolves only to an account Mosaic observed with exactly that handle. Other platforms are 400 invalid_input.
maxAgeMs integer, 0–7 776 000 000 Default 2 592 000 000 (30 days), maximum 90 days. Bounds reuse of provider discovery; 0 asks for fresh provider discovery.
maxCredits integer, 0–1 000 000 Default 0 while the tariff is unpublished.
limit integer, 1–100 Candidates on the first page. Default 25.
idempotencyKey string, 1–255 Required. REST header Idempotency-Key.

An unknown @handle answers resolution_required with no charge. A governed (suppressed or erased) subject, or an unknown creatorId, is 404 not_found.

The common result fields, kind: "creator_socials", and:

  • subject: { id, source: "meta_page" | "youtube_channel" | "creator", externalId, url }.
  • creator: { id, displayName } of the canonical Creator staff confirmed for the subject, or null.
  • confirmedAccounts: the Creator’s staff-confirmed Source Accounts (Meta Pages and YouTube Channels) and staff-approved social links, each { platform, nativeId, nativeIdKind, url, confirmation: { kind: "source_account" | "social_link", confirmedAt } }.
  • candidateAccounts: one page of candidates, { data, nextCursor }. Each candidate carries platform (facebook, instagram, tiktok, x, youtube), nativeId and nativeIdKind (page_id, channel_id, or handle), url, sourceSubject (the researched or confirmed account whose evidence found it), evidenceUrl (a public page showing the evidence), discoveryMethod, observedAt, evidenceCount, and a categorical basis.
  • coverage: methods searched, providerDiscovery (not_run until the tariff is published), status and truncated (capped when the description-link scan hit its bound), and counts of confirmed, candidate, and governance-hidden accounts.
basis Meaning Strength
profile_link_back The candidate’s own profile links to the researched account Linked evidence
explicit_outbound_link The researched account’s profile, or its own video descriptions, link to the candidate Linked evidence
provider_reported An external profile-discovery provider reported it (not composed yet) Provider claim
name_handle_similarity Only its name or handle equals the researched account’s, after removing case and punctuation Weak; corroborate before relying

Candidates are ordered by basis in that order, then by evidenceCount. There are no scores or percentages: none of these is identity confirmation, and only a staff decision moves an account to confirmedAccounts.

discoveryMethod Signal Mosaic already holds
meta_page_profile A Meta Page’s own profile URL and metadata
meta_page_profile_link A Meta Page profile link that Channel Discovery resolved to a canonical YouTube Channel (used in both directions)
youtube_video_description Social profile links in a Channel’s own retained video descriptions (other YouTube Channels are omitted as likely collaborations)
catalog_name_match Source Accounts on the other platform with the same normalized name or handle
provider An external provider (not composed)

Only a recognized profile URL becomes a candidate: posts, videos, share links, link-in-bio pages, websites, and mailto: links never do, and quarantined contacts are never read. Results are read from Mosaic’s signals when the result is read, so observedAt is that read time and polling a request can show a later staff confirmation. YouTube Channel profile links are not retained in a readable form today, so they are not a signal yet.

Answers from Mosaic’s own signals are free (cacheHit: true, creditsCharged: 0) and need no provider. maxAgeMs: 0 asks for fresh external profile discovery, which has no published tariff and is 409 tariff_unpublished.

As get_meta_company_ads, plus 409 tariff_unpublished. A missing or doubled reference, or an unsupported URL, is 400 invalid_input.

POST /v1/youtube/sponsorships · MCP search_youtube_sponsorships · write scope · cached samples free; fresh search at the published tariff (proposed 5 Work Credits), unpublished by default

Searches YouTube for videos that link a company, then reports each video’s signals from its official description. The answer is a bounded sample, not the company’s sponsorship history.

Field Type Notes
domain hostname The company’s own domain. Social, hosting, shortener, redirector, and link-in-bio hosts are invalid_input.
companyName string, 1–200 Optional. Adds one search query, names the company in disclosures, and identifies its own Channel.
maxAgeMs integer, 0–604 800 000 Default 86 400 000 (24 hours). Maximum 7 days.
maxCredits integer, 0–1 000 000 Defaults to the published tariff; while it is unpublished, 0. 0 is cache-only.
limit integer, 1–100 Videos on the first page. Default 25.
idempotencyKey string, 1–255 Required. REST header Idempotency-Key.

A cache hit is a completed sample of the same domain and case-insensitive companyName, under the same plan and classifier version, observed within maxAgeMs by any Workspace.

Each fresh call runs one fixed, bounded plan (youtube-sponsorship-search.v1):

  1. Search. One page of the pinned YouTube search Actor, without descriptions, for the domain, then for companyName when given: at most two queries.
  2. Candidates. Distinct videos and Shorts in query order, then rank order, capped at 50. Live results are excluded. Search rank is where a video was found, never evidence.
  3. Descriptions. One YouTube Data API videos.list call for the candidates: 1 quota unit. Broadcasts are excluded and governed videos are withheld, both counted.
  4. Links. Mosaic’s existing link extraction, then redirect resolution of shorteners, campaign redirectors, affiliate networks (for example shrsl.com, anrdoezrs.net), and app deep links (app.link, sng.link, onelink.me): at most 10 per description and 100 per run. Link-in-bio pages such as linktr.ee are never followed.
  5. Matching. Mosaic’s existing matcher, with the request’s domain as a company-domain rule for this request only. When the domain is a canonical Brand’s primary domain, the Brand’s confirmed hostnames (including exact campaign hosts) also apply. Research never creates a Matcher, admits a Channel, records YouTube documents, or enrolls monitoring.

The domain becomes a research subject (source: "company_domain") with canonicalBrandId set only when it is exactly one active Brand’s primary domain.

A video is listed when its description links the company (match: "brand_link") or has an intermediary link Mosaic could not attribute (match: "unresolved_link_only"). A title mention, search rank, or the paid-promotion flag never lists a video. Each video keeps its signals apart:

Field Meaning
brandLinks Each link that matched, with start (code-point offset), inFirstHalf, matchedVia (link_host or resolved_host), the redirect chain, sharedPlatform, context (description, citation, referral_label), and any affiliate network or parameter
unresolvedLinks Redirector or landing-page links without an attributable destination, with reason: resolution_failed, resolution_skipped, landing_page_not_followed, resolved_to_landing_page, or resolved_to_shared_platform (at most 10; unresolvedLinkCount)
disclosures Lines with explicit sponsorship wording (sponsorship), an explicit denial such as “not sponsored” or “not affiliated with” (denies_sponsorship), or affiliate wording (affiliate), each with namesCompany
firstHalfLink The existing rule youtube.brand_link_first_half.v1 over links that can place a sponsor: inferred, not_inferred, or not_applicable. Shared-platform links and citations are not eligible
paidPromotion YouTube’s creator-declared paid-promotion flag. It names no company
classification Provisional class, or null for unresolved_link_only

A shared platform is an app store listing, the Amazon marketplace, a link-in-bio page, or a shared shortener. A link there is attributed only to the platform’s own company (an App Store link matches apple.com, never the app’s maker) and never supports a sponsorship inference.

classification is { value, provisional: true, basis }. The classes are drafts awaiting labels (acceptance cases section D), under classifierVersion youtube-sponsorship-draft-2026-10-15, decided in this order:

value basis Rule
self_promotion channel_named_as_company The Channel’s title equals the company name or domain label
sponsorship_disclosed disclosure_names_company A sponsorship disclosure line names the company or its linked host
affiliate affiliate_link A non-citation Brand link passes an affiliate network, carries an affiliate parameter, or is labeled as a referral
link_only disclosure_denies_sponsorship The description says it is not sponsored by, or not affiliated with, a company
sponsorship_inferred brand_link_in_first_half firstHalfLink is inferred
link_only brand_link_without_placement_signal Anything else

sponsorship_inferred is an inference from link position, never verified payment. Affiliate and referral links stay distinguishable from sponsorship.

The common result fields, plus:

  • subject: { id, source: "company_domain", domain, companyName, canonicalBrandId }.
  • coverage: status (complete, or capped when the queries found more than 50 candidates), truncated, and sample: the plan version, each query with its result count, the candidate cap, candidatesFound, candidatesExamined, descriptionsAcquired, descriptionsMissing, videosUnavailable, excludedFormats, withheld, searchGaps, linkResolution (attempted, budget, budgetExhausted), and linkLimitHits. Then hidden (governed since the sample), matchedVideos, unresolvedOnlyVideos, counts per class, and a statement. A video whose video or Channel is erased, even while the search runs, is removed from the stored receipt and not counted in hidden; sample counts are unchanged and the search is still charged.
  • videos: one page, matched videos first by class (disclosed, inferred, affiliate, self-promotion, link only), then unresolved-only videos, each newest first; null unless status is succeeded.

There is no resolution_required status: the domain is searched as given. Zero matches means none were found among the videos examined, not that the company has no sponsorships.

As get_meta_company_ads, except:

Status Code When
400 invalid_input domain is not a company hostname, or a value is out of range
409 collection_in_progress A fresh search of the same domain and company name is running. Retry after it with maxAgeMs > 0, for free
409 tariff_unpublished No cached sample is fresh enough and fresh search has no published tariff. Nothing was held or recorded

A failed run answers failure.reason acquisition_failed (a search or description read failed) or coverage_incomplete (a paused source did not recover within 24 hours), with no charge.

With a published tariff, a fresh call holds the tariff, answers pending, and Mosaic’s YouTube worker runs the plan. Each planned query is one run of the pinned search Actor, started at most once per request: a retried job resumes the same run, and a start whose outcome is unknown is never repeated or charged (its hold is released, start_unconfirmed, 24 hours after it was sent). Each run first reserves a $0.02 cost ceiling under Mosaic’s daily Apify budget; when the day’s budget is spent the request waits (up to 24 hours) rather than exceeding it, then fails with coverage_incomplete and no charge. Fresh search is published only on deployments whose YouTube worker runs with unrestricted execution scope.

Measured on real inputs (2026-10): two Actor runs per call, about $0.0034 median and $0.0047 at the high end, 7.6 seconds median and 11 seconds p90; videos.list costs 1 Data API quota unit.

GET /v1/usage · MCP get_usage · read scope · free · implemented locally; not deployed

Returns the Workspace’s credits, every tariff, and its limits in one free read. Call it before paid work and after insufficient_credits.

  • credits: available (prepaid and granted credits a new hold may draw), held (reserved by in-flight work), overageRemaining (0 for prepaid packs), spendable, and spendingFrozen.
  • tariffs: one entry per billable product with status published (with credits and the tariff name) or unpublished (paid calls are refused), and the tools that charge it. get_meta_company_ads is meta_company_ads, at 25 credits. retrieve_brand (brand_profile) and search_youtube_sponsorships (youtube_sponsorship_search) are published when the deployment configures their tariff and can run their fresh work (proposed: 10 and 5 credits), else unpublished. find_creator_socials (creator_socials) is always unpublished: its stored-signal answers are free and provider discovery is not offered.
  • free: calls that never spend credits: cached results, get_research_result, get_usage, and reads.
  • limits: REST requests and MCP calls per minute; the maxCredits rule (maxCreditsDefault: "published_tariff": omitted, maxCredits defaults to the tool’s published tariff; maxCreditsCacheOnly: 0: 0 answers only from cache); the largest maxCredits per call (maxCreditsPerCall); and the signup grant size.
  • creditPacks: the prepaid packs a Workspace billing Admin can buy (id, credits, priceCents, currency). Agents cannot buy credits.
  • automaticOverage: false for prepaid customers; true only for a legacy subscription with overage turned on.

Work Credit lots come from four grant sources, visible in the Work Credit REST history: signup (the one-time 100 free credits of a verified self-serve signup), credit_pack (prepaid, non-expiring), monthly_invoice (legacy subscriptions), and operator.