YouTube Creator Portfolio Planner avatar

YouTube Creator Portfolio Planner

Pricing

from $8.40 / 1,000 creator assessments

Go to Apify Store
YouTube Creator Portfolio Planner

YouTube Creator Portfolio Planner

Build a ranked creator shortlist from YouTube discovery datasets. Deduplicate channels, compare observed seed overlap and subscriber bands, and keep the evidence behind each creator assessment.

Pricing

from $8.40 / 1,000 creator assessments

Rating

0.0

(0)

Developer

Tim Zinin

Tim Zinin

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

YouTube Creator Portfolio Planner — turn a channel-discovery batch into a ranked, evidence-backed shortlist

Feed it an existing YouTube channel-discovery dataset and get back one ranked creator assessment per unique channel — deduplicated seed overlap, a conservative subscriber estimate with its own confidence label, and an audience-band verdict — never an audience-similarity or ROI prediction.

YouTube Creator Portfolio Planner — hero diagram

This Actor does not scrape YouTube. It reads a channel-discovery batch you already have — most naturally from YouTube Channel Lookalike Finder and/or a streamers/youtube-scraper-shaped dataset — and consolidates it into one ranked assessment per unique candidate channel: how many of your seed channels recommended it, a conservative subscriber estimate, whether it falls inside the audience-size band you configured, and a shortlist rank. It is a creator-partnerships research tool for teams working from a discovery batch they already collected — it is not an audience-similarity score, a demographics engine, or a claim about campaign ROI.

What you get

For each distinct candidate channel found across your source batch, you get one assessment record containing:

  • channelId and channelName — stable identity and first accepted non-empty source display name (channelNameConflict: true when the source reported more than one distinct name for one channel ID).
  • seedChannels, seedOverlapCount, and duplicateSeedMentionCount — up to 50 actual seed channels that recommended this candidate, the true unbounded count of distinct seeds behind it, and deduplicated repeat mentions.
  • subscriberCount, subscriberCountApproximate, subscriberCountState, and subscriberCountConflict — a conservative estimate (exact from a native field, otherwise floor-parsed from YouTube's own truncated "1.27M subscribers"-style text), and one of four distinct states — observed, hidden, invalid, or missing — never conflated (see Evidence and boundaries).
  • audienceBand, status, and action — your configured band and whether this candidate falls within_audience_band (→ shortlist), outside_audience_band (→ deprioritize), or audience_size_unknown (→ review).
  • shortlistRank — a deterministic rank across the whole delivered batch: band fit first, then seed overlap, then subscriber count.
  • evidence — the matched value, resolving rule, and source row — up to 12 entries, subscriber evidence always retained before optional seed references, truncation itself disclosed.
  • topics — always the literal string "no_topics_available": never fabricated.
  • Honest diagnostics for everything else — a row with neither a similarChannelId nor a channelId, an invalid seed identity, or a channel with no usable signal and no seeds at all are all free, separate rows.

What this Actor is explicitly not: no audience-overlap percentage, no demographic breakdown, no engagement or ROI prediction. seedOverlapCount reflects how many of your own seed channels recommended this candidate — not evidence the two channels share real viewers.

Who uses it

  • Creator partnerships and influencer-marketing teams consolidating "similar channels" discovery pulls across several seed creators into one ranked shortlist, instead of manually cross-referencing spreadsheets.
  • Talent scouts and channel researchers who want a repeatable audience-band filter (e.g. "only channels between 100K and 2M subscribers") applied consistently across a batch.
  • Sponsorship and media-buying teams who need a defensible, evidence-linked reason a channel made the shortlist, rather than an opaque score.
  • Data-quality teams auditing an upstream discovery scrape, using subscriberCountState/subscriberCountConflict as a signal for how clean the underlying data is.

Not a scraper: point it at an existing discovery Dataset producing the row shape in Input contract, most naturally YouTube Channel Lookalike Finder (users30d=9, successfulRuns30d=57) or a streamers/youtube-scraper-shaped dataset (users30d=10475, successfulRuns30d=2,425,614). This Actor never starts either one, and never fetches a page from the open web.

How to run

  1. Console (fastest for a one-off run). Open the Actor, go to Input, paste rows into Source records (inline) or fill in Source Dataset, leave defaults, hit Start.
  2. From an existing discovery Dataset. Put its Dataset ID in datasetId — read with your own run's permissions, no separate authorization step.
  3. Chained via Integrations. Add this Actor as an "Actor/task run" integration on your upstream Actor; map {{resource.defaultDatasetId}} into this Actor's datasetId field explicitly (see Integration recipes) for a READ grant — a bare Dataset ID or webhook payload alone does not grant access.
  4. Via the API. Call POST /v2/acts/dRa5f5Qv1jdKIs5fJ/runs (or run-sync-get-dataset-items) with datasetId (or items) and, for a spending ceiling, the run option maxTotalChargeUsd.
  5. Set your audience band. audienceMin/audienceMax define the eligibility window (default 0 to unbounded); out-of-band candidates are still delivered, never dropped.
  6. Read the results. One row per unique candidate lands in the run's default Dataset, sorted by shortlistRank, with a matching OUTPUT/SUMMARY record.

Every run is independent processing — see Operating guide for what that means for re-running against the same batch.

Pricing

Pay-per-event, one event type: result-found ("Creator assessment" in the platform's pricing UI — the primary pricing noun used throughout this listing, the Dataset terminology, and the pricing record itself) — one delivered creator assessment (one unique channel). There is no start fee, and every free diagnostic (a missing identity, an insufficient-data row, a budget stop, an empty batch) requests no charge event at all.

TierPrice per creator assessmentDiscount
Free (default)$0.012—
Bronze$0.010810%
Silver$0.009620%
Gold$0.008430%
Platinum$0.008430%
Diamond$0.008430%

These are Apify's platform-wide account pricing tiers — not a volume schedule this Actor invents — assigned automatically by your account's usage; you don't select one. One creator assessment is one unique channel across the whole batch, never billed per discovery row.

A note on the evidence in this README: every output example below comes from the product owner's own verification runs against this Actor's accepted build — cost to the owner, not sales or customer revenue.

Set the platform's own Max total charge (USD) run option to cap spend. If testing the zero-paid path via a raw API call, do not rely on literal maxTotalChargeUsd: 0 — use a small positive value below every tier's price (e.g. $0.005, below Gold/Platinum/Diamond's $0.0084) instead — see Evidence and boundaries.

Input contract

FieldTypeDefaultNotes
itemsarray (JSON editor), max 1000 entriesprefilled real exampleInline source records. Ignored if datasetId, runId, or a resolvable payload.resource is also given.
datasetIdstring (Dataset picker, READ)—Dataset ID from a channel-discovery run. Read with your own run's permissions. Overrides inline items.
runIdstring, max 64 chars—An Actor run ID to read instead of a Dataset ID; its default Dataset resolves automatically under your own permissions — no upstream Actor started.
payloadobject (JSON editor)—Optional ACTOR.RUN.SUCCEEDED-shaped payload carrying resource.defaultDatasetId/resource.id. The datasetId resource picker is the recommended way to grant private-Dataset access; a bare payload reference alone does not.
maxInputRowsinteger100Hard cap on source records examined before grouping (1–1000). Still bounded to 4 MiB total / 64 KiB per row.
maxResultsinteger10Max useful creator-assessment results this run can deliver (1–1000), independent of the platform's Max total charge option.
audienceMininteger0Lower band bound. Below-band candidates are still delivered, marked outside_audience_band.
audienceMaxintegerunboundedUpper band bound; empty means no cap. Both bounds: non-negative safe integers ≤10,000,000,000 (a disclosed engineering ceiling), audienceMin ≤ audienceMax — else rejected as invalid input.

Source precedence, exactly: an explicit datasetId or payload.resource.defaultDatasetId (they must agree if both are present) beats a runId or payload.resource.id, which beats inline items. A dataset/run source is never merged with inline rows. A malformed or conflicting reference is rejected as invalid input, never silently downgraded to the inline example. Private-Dataset READ access is conditional on the resource picker or an authorized Integrations mapping — a bare Dataset ID string, or a webhook payload's resource.defaultDatasetId value alone, does not grant access to data you aren't already permitted to read.

Accepted row fields (any subset present is used; nothing here is strictly required except a channel identity — see Evidence and boundaries for what happens with less):

FieldAccepted asRole
similarChannelIdstring, ≤128 charsA discovery-edge row: recommended on a seed channel's About page. Paired with similarChannelName/similarSubscriberCountText.
requestedChannelId / requestedChannelstring, ≤128 chars / stringThe seed channel behind this edge. Over 128 chars, it's excluded (invalid_seed_identity), never truncated into a false match.
channelIdstring, ≤128 charsA native enrichment row: the candidate's own ID, used when similarChannelId is absent. Paired with channelName/numberOfSubscribers.
similarSubscriberCountTextstringYouTube's own truncated display text (e.g. "1.27M subscribers"), floor-parsed, never rounded. An explicit "hidden" mention is a distinct disclosed-hidden signal, not an unparseable value.
numberOfSubscribersintegerExact subscriber count from a native source. Always preferred over display-text parsing when valid.
checkedAtstring (ISO 8601)Resolves conflicting subscriber observations — the most recently, validly timestamped wins; a calendar-invalid timestamp never wins (see Evidence and boundaries).
found, error, errors, failed, statusvariousfound: false, failed: true, or a recognized failure-status word (failed/error/timed-out/aborted) is ALWAYS a free diagnostic, even alongside found: true/partial: true on the same row. Only a non-empty error/errors value with none of those hard-failure signals present is spared when the row also carries found: true and partial: true, read as disclosed incompleteness on otherwise-usable data rather than a hard failure.

Neither similarChannelId nor channelId present on a row is a free missing_channel_id diagnostic — a bare video ID is deliberately never treated as a channel identity.

Hard read bounds, always enforced regardless of maxInputRows: at most 1000 source rows, at most 4 MiB of total source JSON per run, at most 64 KiB per individual row (an oversized row is dropped and disclosed). This Actor reads only the fixed https://api.apify.com origin with your run's own token — it never fetches an arbitrary URL and never starts an upstream Actor.

Output examples

Every example below is a real record from an actual accepted run of this Actor's build 0.1.3 (Actor ID dRa5f5Qv1jdKIs5fJ, build PvVOx0wCVVUPpuJv7), reproduced exactly — nothing is hand-edited. Source dates come from checkedAt (2026-09-22T05:46:32.738Z); the runs were processed 2026-09-23 — a saved snapshot, not a live re-check.

1. First useful assessment — a full, real dataset row

This run used the Actor's own real inline prefill: 5 real "similar channels" discovery rows for seed channel @mkbhd, default audienceMin/audienceMax. Both diagrams on this page illustrate this exact run — the same 5 real candidates, all delivered as paid results.

Actor input (the Actor's own real prefill; all 5 rows share this shape):

{
"items": [
{
"requestedChannel": "@mkbhd",
"requestedChannelId": "UCBJycsmduvYEL83R_U4JriQ",
"requestedCanonicalChannelUrl": "http://www.youtube.com/@mkbhd",
"similarChannelId": "UC2J-0g_nxlwcD9JBK1eTleQ",
"similarChannelName": "Auto Focus",
"similarSubscriberCountText": "1.27M subscribers",
"widgetPosition": 2,
"source": "about-page-similar-channels-widget",
"status": "ok",
"confidence": "display-text",
"partial": false,
"action": "ingest",
"error": null,
"checkedAt": "2026-09-22T05:46:32.738Z",
"_exampleProvenance": {
"sourceActorId": "sMokIFjXOK7W7zbfU",
"sourceRunId": "cJ6Tezu4odFHY9tDM",
"sourceDatasetId": "wm2PnJsPBGeG68p3T",
"curatedPublicSnapshot": true,
"redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities."
}
}
]
}

First Dataset output row (of five delivered from this run — the same public source Dataset 3XjcYLjp2T3gGx6d2 used via datasetId independently confirmed the identical result, also 5 delivered):

{
"entityId": "channel:UC2J-0g_nxlwcD9JBK1eTleQ",
"found": true,
"status": "within_audience_band",
"action": "shortlist",
"confidence": "approximate_display_text",
"sourceRowCount": 1,
"channelNameConflict": false,
"channelId": "UC2J-0g_nxlwcD9JBK1eTleQ",
"channelName": "Auto Focus",
"seedChannels": [
{
"seedChannelId": "UCBJycsmduvYEL83R_U4JriQ",
"seedDisplay": "@mkbhd",
"mentionCount": 1,
"sourceRowIndex": 1
}
],
"seedOverlapCount": 1,
"seedEvidenceTruncated": false,
"duplicateSeedMentionCount": 0,
"subscriberCount": 1270000,
"subscriberCountApproximate": true,
"subscriberCountHidden": false,
"subscriberCountState": "observed",
"subscriberCountConflict": false,
"subscriberSelectionReason": "most_recent_checked_at",
"audienceBand": {
"min": 0,
"max": null
},
"topics": "no_topics_available",
"observedAt": "2026-09-22T05:46:32.738Z",
"sourceObservationWindow": {
"earliest": "2026-09-22T05:46:32.738Z",
"latest": "2026-09-22T05:46:32.738Z",
"validCount": 1,
"totalRowCount": 1
},
"shortlistRank": 1,
"evidence": [
{
"field": "similarSubscriberCountText",
"value": "1.27M subscribers",
"rule": "conservative_display_text_parse:most_recent_checked_at",
"sourceRowIndex": 1
},
{
"field": "requestedChannelId",
"value": "@mkbhd (UCBJycsmduvYEL83R_U4JriQ)",
"rule": "seed_recommended_candidate",
"sourceRowIndex": 1
}
],
"businessPartial": false,
"sourceRowIndexes": [
1
],
"partial": false,
"sourceCoverage": {
"complete": true,
"reason": null,
"droppedOversized": 0
},
"product": "youtube-creator-portfolio-planner",
"schemaVersion": "1.0",
"sourceDatasetId": null,
"sourceRunId": null,
"suppliedSourceRunId": null,
"sourceRunIdVerified": null,
"processedAt": "2026-09-23T04:44:11.135Z"
}

"1.27M subscribers" is YouTube's own truncated display text, floor-parsed to 1,270,000 — never rounded up. shortlistRank: 1 reflects this being the top-ranked candidate in this 5-channel batch (all tied seedOverlapCount: 1, within the default unbounded band).

2. Derived partial-flag control — same real values, an explicit incompleteness flag injected

This is not a demonstration of removed columns or actual upstream data incompleteness. It is the identical real discovery batch as example 1, with each source row's own partial field deliberately set to true (and found: true added), to exercise this Actor's disclosed-incompleteness path under a controlled, known input. Every underlying value is identical and real; only the incompleteness flag is a deliberate test control, not an observed condition from any actual scrape.

Actor input (identical to example 1, except "partial": true, "found": true added):

{
"items": [
{
"requestedChannel": "@mkbhd",
"requestedChannelId": "UCBJycsmduvYEL83R_U4JriQ",
"requestedCanonicalChannelUrl": "http://www.youtube.com/@mkbhd",
"similarChannelId": "UC2J-0g_nxlwcD9JBK1eTleQ",
"similarChannelName": "Auto Focus",
"similarSubscriberCountText": "1.27M subscribers",
"widgetPosition": 2,
"source": "about-page-similar-channels-widget",
"status": "ok",
"confidence": "display-text",
"partial": true,
"action": "ingest",
"error": null,
"checkedAt": "2026-09-22T05:46:32.738Z",
"_exampleProvenance": {
"sourceActorId": "sMokIFjXOK7W7zbfU",
"sourceRunId": "cJ6Tezu4odFHY9tDM",
"sourceDatasetId": "wm2PnJsPBGeG68p3T",
"curatedPublicSnapshot": true,
"redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities."
},
"found": true
}
],
"maxInputRows": 100,
"maxResults": 10
}

Resulting row (businessPartial: true, partial: true — otherwise identical to example 1's row):

{
"entityId": "channel:UC2J-0g_nxlwcD9JBK1eTleQ",
"found": true,
"status": "within_audience_band",
"action": "shortlist",
"confidence": "approximate_display_text",
"sourceRowCount": 1,
"channelNameConflict": false,
"channelId": "UC2J-0g_nxlwcD9JBK1eTleQ",
"channelName": "Auto Focus",
"seedChannels": [
{
"seedChannelId": "UCBJycsmduvYEL83R_U4JriQ",
"seedDisplay": "@mkbhd",
"mentionCount": 1,
"sourceRowIndex": 1
}
],
"seedOverlapCount": 1,
"seedEvidenceTruncated": false,
"duplicateSeedMentionCount": 0,
"subscriberCount": 1270000,
"subscriberCountApproximate": true,
"subscriberCountHidden": false,
"subscriberCountState": "observed",
"subscriberCountConflict": false,
"subscriberSelectionReason": "most_recent_checked_at",
"audienceBand": {
"min": 0,
"max": null
},
"topics": "no_topics_available",
"observedAt": "2026-09-22T05:46:32.738Z",
"sourceObservationWindow": {
"earliest": "2026-09-22T05:46:32.738Z",
"latest": "2026-09-22T05:46:32.738Z",
"validCount": 1,
"totalRowCount": 1
},
"shortlistRank": 1,
"evidence": [
{
"field": "similarSubscriberCountText",
"value": "1.27M subscribers",
"rule": "conservative_display_text_parse:most_recent_checked_at",
"sourceRowIndex": 1
},
{
"field": "requestedChannelId",
"value": "@mkbhd (UCBJycsmduvYEL83R_U4JriQ)",
"rule": "seed_recommended_candidate",
"sourceRowIndex": 1
}
],
"businessPartial": true,
"sourceRowIndexes": [
1
],
"partial": true,
"sourceCoverage": {
"complete": true,
"reason": null,
"droppedOversized": 0
},
"product": "youtube-creator-portfolio-planner",
"schemaVersion": "1.0",
"sourceDatasetId": null,
"sourceRunId": null,
"suppliedSourceRunId": null,
"sourceRunIdVerified": null,
"processedAt": "2026-09-23T04:44:28.438Z"
}

Notice the assessment itself is unchanged from example 1; only businessPartial/partial flip to true because the source row disclosed its own incompleteness — exactly what a genuinely partial real scrape would produce.

3. Below-price run — free budget_stopped diagnostic, no partial charge

[
{
"product": "youtube-creator-portfolio-planner",
"schemaVersion": "1.0",
"status": "budget_stopped",
"reason": "Run charge limit cannot cover any result; no source was read.",
"found": false,
"processedAt": "2026-09-23T04:44:41.773Z"
}
]

This run used the platform run option maxTotalChargeUsd: 0.005 — below every tier's per-assessment price (even Gold/Platinum/Diamond's $0.0084). The Actor never reads the source batch, delivers zero paid assessments, and writes this one free diagnostic. See Evidence and boundaries for the API-level zero-cap caveat.

4. Empty batch — free no_results diagnostic

Actor input:

{ "items": [], "maxInputRows": 100, "maxResults": 10 }

Dataset output:

[
{
"product": "youtube-creator-portfolio-planner",
"schemaVersion": "1.0",
"status": "no_results",
"found": false,
"reason": "no_usable_rows_in_batch",
"sourceCoverage": {
"complete": true,
"reason": null,
"droppedOversized": 0
},
"processedAt": "2026-09-23T04:44:49.606Z"
}
]

An empty or entirely-unusable batch is a free diagnostic, never a silent zero-row success and never a charge.

Field dictionary

Per-assessment result fields:

FieldTypeMeaning
entityIdstring"channel:<channelId>" — stable key for this candidate.
foundbooleantrue on every billed assessment; false on every free diagnostic.
statusstringwithin_audience_band, outside_audience_band, or audience_size_unknown on an assessment; a diagnostic reason (invalid_row, source_reported_failure, missing_channel_id, invalid_seed_identity, insufficient_data, invalid_input, budget_stopped, no_results, max_results_reached, result_dropped_oversized, source_rows_dropped, partial_source, source_unavailable) on a free row.
actionstringshortlist (within band), deprioritize (outside band), or review (unknown count).
channelId / channelNamestringThe candidate's stable ID and first accepted non-empty source display name.
channelNameConflictbooleantrue if the source reported more than one distinct name — the true, unbounded set, not just the up-to-5 names stored for display.
seedChannelsarrayUp to 50 { seedChannelId, seedDisplay, mentionCount, sourceRowIndex } entries.
seedOverlapCountnumberThe TRUE, unbounded count of distinct seed channels, even when seedChannels display is truncated.
duplicateSeedMentionCountnumberRepeated mentions of the same seed, deduplicated out of seedOverlapCount.
seedEvidenceTruncatedbooleantrue when the 12-slot evidence budget couldn't fit every seed's evidence — subscriber evidence is always retained first.
subscriberCountnumber/nullThe resolved estimate, or null when subscriberCountState is hidden, invalid, or missing.
subscriberCountApproximatebooleantrue when parsed from display text rather than an exact numeric field.
subscriberCountStatestringobserved, hidden (source explicitly said hidden), invalid (present but unparseable/out of bound), or missing (no field at all) — never conflated.
subscriberCountHiddenbooleanConvenience flag mirroring subscriberCountState === "hidden".
subscriberCountConflictbooleantrue when multiple rows reported different values for the same channel.
subscriberSelectionReasonstring/nullmost_recent_checked_at (valid timestamp broke the tie) or first_source_row (no valid timestamp existed).
audienceBandobject{ min, max } — the effective band this run applied.
topicsstringAlways the literal "no_topics_available" — never inferred or fabricated.
shortlistRanknumber1-based rank: audience-band fit, then seedOverlapCount, then subscriberCount.
evidencearrayUp to 12 entries, each { field, value, rule, sourceRowIndex }.
sourceRowCount / sourceRowIndexesnumber / arrayTrue count and (capped at 50) list of contributing source rows.
businessPartial / partialbooleanbusinessPartial is this module's own signal (disclosed partial, a subscriber conflict/invalid observation, a truncated list, or more names than displayed). partial also folds in run-level source incompleteness — check partial if you only check one.
sourceCoverageobjectRun-level: { complete, reason, droppedOversized }.
sourceDatasetId, sourceRunId, suppliedSourceRunId, sourceRunIdVerifiedstring/null, boolean/nullWhere the batch came from; sourceRunId is set only when independently verified.
observedAtstring / nullLatest strictly valid source checkedAt among all accepted rows contributing to this channel; null if unknown. No processing-time fallback.
sourceObservationWindowobjectearliest and latest preserve source timestamp strings; validCount counts valid capture timestamps and totalRowCount counts all contributing accepted rows, before the 50-reference display cap. Missing/invalid timestamps do not advance the window.
processedAtstring (ISO 8601)When this Actor processed the row, separate from source capture time.
reason / errorstringPresent on free diagnostic rows only.

Diagnostic-only Dataset fields: mode identifies the selected source mode on invalid_input/source_unavailable; count counts represented source/oversized rows; delivered and totalUseful report confirmed deliveries and candidates on cap notices. A source_unavailable notice accompanies a FAILED run. These notices have found:false and request no result event.

Complete runtime OUTPUT/SUMMARY field table (the Key-value store record under both the OUTPUT and SUMMARY keys — identical payload, two conventional key names; no metadata field exists in this runtime's output):

FieldMeaning
schemaVersionThis product's output schema version string ("1.0").
product"youtube-creator-portfolio-planner".
outcomedelivered, budget_stopped, max_results_reached, no_results, diagnostic, or failed.
errorCodeShort internal code on a non-nominal outcome (e.g. SOURCE_UNAVAILABLE), or null.
fatalFailuretrue only when outcome === "failed".
deliveryUncertainA Dataset-write or charge acknowledgment is uncertain, including FREE_WRITE_FAILED; raw rows can exceed confirmed counters.
replayBlockedtrue on either of two distinct refusal causes: the run's own result-found event counter didn't match its expected record (EXISTING_RESULT), or this run's claim key was already set from a prior attempt and automatic resurrection is unsupported (EXISTING_CLAIM) — either way, refusing to bill further avoids a duplicate charge.
requestedSource rows actually read this run.
usefulResultsResults found after oversized-row filtering, before budget/maxResults cap.
deliveredPaid, confirmed assessments actually written.
paidConfirmed result-found charge events, cross-checked against delivered.
freeFree diagnostic rows actually written — see Evidence and boundaries for how this can exceed the 200-row cap.
failedUseful results dropped for exceeding the 64 KiB per-row limit (rare).
sourcePartial / sourcePartialReasonIncomplete source read: max_input_rows_reached, total_byte_cap_reached, oversized_rows_dropped, pagination_interrupted, invalid_json_page, unexpected_shape or unexpected_page_size; otherwise null.
sourceDroppedOversizedSource rows dropped for exceeding 64 KiB before reaching assessment logic.
budgetStoppedtrue if the charge limit was reached before all useful assessments were delivered.
resultsCappedtrue if maxResults was reached before all useful assessments were delivered.
businessPartialResultsPartial transform results counted before size, budget and result-cap filtering; not a delivered-result count.
oversizedDiagnosticsDroppedDiagnostic rows dropped for exceeding 64 KiB (rare).
diagnosticsTruncatedThe combined initial list of business diagnostics and source-read notices exceeded 200; later result-size/cap notices are outside this limit.
partialOveralltrue if any of the above fired — check this one field if you only check one.
budgetModeunlimited, finite, or local_no_charging (running outside the platform).
maxTotalChargeUsdThe effective run charge cap enforced, or null when unlimited/local.
replaySafetyFixed disclosure string: re-running is new processing, no cross-run dedup.
businessSummary{ rowsProcessed, resultsCount, diagnosticsCount, withinAudienceBandCount, outsideAudienceBandCount, audienceSizeUnknownCount, uniqueSeedCount, audienceBand, observedAt, sourceObservationWindow, sourceDatasetId, sourceRunId } — uniqueSeedCount is the true unbounded seed identity, not the display-capped seedChannels list.
processedAtISO 8601 timestamp of this OUTPUT/SUMMARY write.

Evidence and boundaries

Two distinct identity paths, never confused. A discovery-edge row (similarChannelId present) and a native channel-enrichment row (channelId present, no similarChannelId) are handled by entirely separate logic — a bare video ID is never treated as a channel identity, and a row with neither is a free missing_channel_id diagnostic.

Seed overlap is always counted on the true, unbounded set — never on the display slice. seedOverlapCount reflects every distinct seed channel actually observed for a candidate, even when the displayed seedChannels array is capped at 50 — ranking uses the true count, not the display prefix.

Subscriber evidence is never crowded out by seed evidence. The core subscriber-resolution evidence entry is always pushed into the 12-slot evidence budget before any individual seed-recommendation evidence, and a seed_evidence_truncated disclosure entry reserves its own slot rather than silently dropping.

A seed ID over the 128-character identity bound never contaminates the real count. It is excluded outright before overlap counting, summary totals, or output — it produces only an invalid_seed_identity diagnostic, never quietly counted toward seedOverlapCount.

Four subscriber states, never conflated. hidden means the source explicitly said so (an actual "...subscribers hidden"-style signal); invalid means a subscriber field was present but failed to parse or exceeded the disclosed 10-billion engineering bound; missing means no subscriber field was present in any accepted row for this channel at all; observed means a usable value was resolved (exact or approximate). A hidden-count channel is never relabeled as missing, and vice versa.

The channel observation window covers every accepted source row, including seed-discovery rows without a subscriber field. Source-failed rows do not contribute. The batch businessSummary.observedAt and businessSummary.sourceObservationWindow describe all accepted channel observations; their scope is broader than a single delivered result. Capture timestamps are validated for calendar, timezone and up to nine fractional digits, with exact fractional ordering. Equivalent-instant window ties use a deterministic raw-string choice. This window describes source capture, not a guarantee that every selected field was measured simultaneously: the name can still come from the first accepted observation, while subscriber values follow the timestamp selection rule below. Dates are never invented from job execution time.

Conflict resolution never fabricates freshness. When multiple rows report different subscriber values for one channel, the candidate with the highest calendar-valid checkedAt wins — a lexically larger but impossible date (like a rolled-forward February 30th) never beats a genuinely valid earlier timestamp, since every component is checked against the real calendar, not just string comparison. When no candidate has any valid timestamp, the first source row wins deterministically, disclosed as first_source_row, never silently presented as most_recent_checked_at.

Hard failures always override a contradictory found:true, partial:true claim. A row with a genuinely disqualifying failure signal is treated as a diagnostic even if it also carries found: true/partial: true.

Bounded, disclosed caps, applied only to display, never to the underlying count: seedChannels capped at 50 per candidate (seedOverlapCount is the true total); evidence capped at 12 entries; sourceRowIndexes capped at 50 (sourceRowCount is the true total); channelNames display capped at 5 (channelNameConflict reflects the true, unbounded distinct-name set); bounded text fields capped at 300 characters.

The initial diagnostic list writes at most 200 entries; total free rows can exceed that. This list combines business diagnostics with partial_source and source_rows_dropped notices. businessSummary.diagnosticsCount counts business diagnostics only, while diagnosticsTruncated covers the combined initial list. Later oversized-result and result/budget-cap notices are outside that cap. Verified against a real 1,000-row batch (995 synthetic source_reported_failure control rows plus the real 5-channel batch, maxResults: 1): exactly 200 business diagnostics were written, one further max_results_reached notice was added, for 201 total free rows (freeRows: 201 in that run's own settled.json) — never described as "capped at 200 total." The single correctly-ranked paid result (Auto Focus) was still delivered correctly, untouched by the 995 preceding synthetic failures.

Budget behavior — read this before relying on a specific maxTotalChargeUsd value via the API. A positive maxTotalChargeUsd below the price of one assessment correctly delivers zero paid assessments and one free budget_stopped diagnostic (output example 3, at $0.005 against a $0.0084 floor tier). Separately, the platform's API-level cap-normalization behavior means a raw maxTotalChargeUsd: 0 set via a direct API call is not guaranteed to be honored as a literal zero-spend instruction. When testing the zero-paid path via a direct API call, do not rely on the literal value 0 — inspect the run's actual, effective options.maxTotalChargeUsd after it starts, and prefer a small positive value below every applicable tier's price (as in example 3) for certainty.

Delivery is not transactional, and a FAILED run does not mean "nothing happened." Each assessment is written to the Dataset and then charged as two separate steps; a run failing between them can leave an effectively free, unconfirmed write with no automatic retry — no cross-run exactly-once guarantee. usefulResults reflects assessments found before budget/result-cap application; failed reflects oversized-row drops (over 64 KiB); paid/delivered reflect only confirmed, charged deliveries. If pricing, charge-limit, or event-counter state can't be verified mid-run, the run finalizes FAILED with a safe explanation — fatalFailure/deliveryUncertain/partialOverall tell you which situation applies. Inspect the original run's Dataset and OUTPUT/SUMMARY record before a manual retry — a blind retry after an ambiguous outcome can duplicate paid assessments, since this Actor does not deduplicate across runs.

Decision routing

  • status: within_audience_band, action: shortlist — resolved subscriber count falls inside your band. shortlistRank orders these by seed overlap, then subscriber count.
  • status: outside_audience_band, action: deprioritize — a resolved count exists but falls outside your band. Still delivered, ranked after within-band and unknown-audience candidates.
  • status: audience_size_unknown, action: review — no subscriber value resolved (subscriberCountState is hidden, invalid, or missing). Ranked after within-band candidates and before outside-band candidates.
  • subscriberCountConflict: true — inconsistent values across rows for the same channel. Check subscriberSelectionReason for how it resolved.
  • businessPartial: true / partial: true — disclosed incompleteness, a conflict/invalid observation, or a display cap was hit — worth a second glance.
  • Free diagnostic rows (found: false) — never an assessment. missing_channel_id, insufficient_data, invalid_seed_identity explain why a row was excluded; budget_stopped/max_results_reached mean a run-level cap was hit — re-run with a higher cap.

Commercial playbooks

Cross-seed shortlist consolidation. Run a discovery pull against several seed creators at once and let this Actor deduplicate every candidate to one ranked row per channel — seedOverlapCount shows which candidates were recommended by more than one seed.

Audience-band filtering per campaign tier. Set audienceMin/audienceMax to your target range and treat action: shortlist rows as your working list — outside_audience_band rows stay visible for a future tier, never discarded.

Escalating "review" candidates. Filter to action: review for a worklist of channels whose subscriber count couldn't be resolved — distinct from channels that are simply outside your band.

Data-quality triage. A high proportion of invalid/missing subscriber states signals your upstream discovery source is inconsistently capturing subscriber data — independent of what this Actor delivers.

Integration recipes

YouTube Creator Portfolio Planner — workflow diagram

This Actor's ID is dRa5f5Qv1jdKIs5fJ. A natural upstream source is YouTube Channel Lookalike Finder (Actor ID sMokIFjXOK7W7zbfU); a streamers/youtube-scraper-shaped native channel source works identically. The recipes below describe how to configure each mechanism — not a claim that any specific trigger is currently installed. A saved public example is available in the Tasks tab.

1. Console-native chaining (no code). On your discovery Actor's run page, open Integrations, add an "Actor/task run" action targeting this Actor, and let the Integrations UI populate the payload with {{resource.defaultDatasetId}}. Map that value explicitly into this Actor's datasetId field — not the generic payload pass-through alone — to request READ access under LIMITED_PERMISSIONS; the caller must already be authorized, and a private source can still deny access.

2. Direct webhook (POST /v2/webhooks), full recipe. Configure a webhook on your upstream discovery Actor for the ACTOR.RUN.SUCCEEDED event, scoped to that specific Actor via its condition.actorId, targeting this Actor's run-creation endpoint:

{
"isApifyIntegration": true,
"shouldInterpolateStrings": true,
"eventTypes": ["ACTOR.RUN.SUCCEEDED"],
"condition": { "actorId": "sMokIFjXOK7W7zbfU" },
"requestUrl": "https://api.apify.com/v2/acts/dRa5f5Qv1jdKIs5fJ/runs?maxTotalChargeUsd=0.50",
"doNotRetry": true,
"headersTemplate": "{\"Authorization\": \"Bearer <APIFY_TOKEN>\"}",
"payloadTemplate": "{\"datasetId\": \"{{resource.defaultDatasetId}}\", \"payload\": {\"resource\": {\"defaultDatasetId\": \"{{resource.defaultDatasetId}}\", \"id\": \"{{resource.id}}\"}}, \"audienceMin\": 100000}"
}

Submit this to POST https://api.apify.com/v2/webhooks with your own Authorization: Bearer <APIFY_TOKEN> header. The <APIFY_TOKEN> placeholder is never a literal token embedded in the template — it is filled in from your own secrets-provisioning store at the moment the webhook is registered. doNotRetry: true avoids a duplicate-charge risk from an automatic retry. datasetId is set explicitly — the resource picker or authorized mapping can request READ access, but the field alone grants nothing; payload.resource alone does not establish access. A positive maxTotalChargeUsd on the request URL caps this chained run's spend; avoid the literal value 0 (see Evidence and boundaries).

3. Generic automation tools (n8n, Zapier, Make, or your own tooling). Use a generic Apify/HTTP node to call run-sync-get-dataset-items on this Actor's ID with a JSON body containing datasetId and your chosen audienceMin/audienceMax/maxResults — a plain authenticated HTTPS POST, no native Apify-integration support required. The same isApifyIntegration/payloadTemplate shape from recipe 2 applies to any integration definition your own tooling submits.

4. Export / CRM-adjacent mapping. Every run's Dataset supports CSV/Excel/JSON export via the Console or GET /v2/datasets/{datasetId}/items?format=csv. channelId, channelName, seedOverlapCount, subscriberCount, status, and shortlistRank alone drive a working creator shortlist in a spreadsheet or CRM import.

Operating guide

  1. Confirm your source batch shape first. Spot-check rows against Input contract — each needs either similarChannelId or channelId, or you'll mostly get missing_channel_id diagnostics.
  2. Set your audience band deliberately. Both in-band and out-of-band candidates are always delivered and billed — a narrow band changes action labels, not your bill.
  3. Start with a small maxResults on a new source to confirm output matches expectations before a full batch.
  4. Set maxTotalChargeUsd via the platform run option, and check its effective value after the run starts. For a guaranteed-zero-paid API call, use a small positive value below $0.0084, not literal 0 — see Evidence and boundaries.
  5. Read the OUTPUT/SUMMARY record, not just the Dataset row count — it reports delivered/paid/free/failed, whether budgetStopped/resultsCapped ended the run early, and diagnosticsTruncated.
  6. If a run finalizes FAILED, inspect its Dataset and OUTPUT record before retrying — a blind retry can duplicate already-successful paid assessments.
  7. Re-running is always new billing — no cross-run dedup key. Reserve it for a new or updated batch, not a routine safety retry.

FAQ

Does this Actor scrape YouTube itself? No. It only reads an existing channel-discovery batch you or an upstream Actor already collected — no open-web fetch, no upstream Actor started.

Can I use a source other than YouTube Channel Lookalike Finder? Yes — any source producing similarChannelId discovery-edge rows or channelId native enrichment rows works, including a streamers/youtube-scraper-shaped dataset.

Does seedOverlapCount mean the two channels share real viewers? No. It reflects how many of your seed channels' "similar channels" widgets recommended this candidate — a discovery-overlap signal, not audience similarity.

What's the difference between hidden, invalid, and missing subscriber states? hidden is an explicit source signal; invalid is a present-but-unparseable/out-of-bound field; missing is no field at all. Never conflated with each other.

Does re-running the same batch avoid double-billing? No. Every run is independent with no cross-run deduplication.

Is the "partial-columns" style example a demonstration of missing upstream data? No — see output example 2. Same real values as the healthy example; only partial/found are deliberately set as a controlled test, not a simulation of real column removal.

Why did one run report 201 free rows instead of the usual 200-row diagnostic cap? The initial business-diagnostic plus source-read-notice list is capped at 200 entries, but later result-size/cap notices are additional — see Evidence and boundaries.

Sources and rights

This Actor reads only Datasets and runs your own Apify account token already has permission to read; it never uses another user's credentials and never starts an upstream Actor on your behalf. The public prefilled example is a curated, non-sensitive real snapshot of a YouTube channel-discovery run for seed channel @mkbhd (sourceActorId sMokIFjXOK7W7zbfU, sourceRunId cJ6Tezu4odFHY9tDM, sourceDatasetId wm2PnJsPBGeG68p3T), observed 2026-09-22, reused with its original values unchanged — a saved example, not a live re-check. All output values in this README are copied verbatim from real runs of this Actor's accepted build; none are illustrative or invented. These verification runs were the product owner's own cost, not customer sales or revenue evidence.

Related tools for adjacent workflows in social platforms and regional directories.

ActorWhat it does
YouTube Channel Lookalike FinderPair it in the social platforms and regional directories workflow: Find the similar/lookalike channels YouTube itself shows on any channel's About page: channelId, name,...
YouTube Channel Videos ListPair it in the social platforms and regional directories workflow: List up to 100 most recent uploads for any YouTube channel: videoId, title, view count, and publish age...
YouTube Channel IntelPair it in the social platforms and regional directories workflow: Public intel card for any YouTube channel: subscribers, total views, video count, join date, country, full...

| YouTube Scraper | Collect video research before assembling a channel batch; map channel fields to this Actor's input contract. | | Fast YouTube Channel Scraper | Collect channel research for a later assessment; validate fields against this Actor's contract before chaining. |