Playbook

Find brands in one video

Search one video for your brands, by content id or by link.

10 min 6 steps

Zelf Detection finds brands in social videos. It reads the whole video and finds each scene where a brand is identifiable: its logo or name is visible (on a product, on packaging or in on-screen text) or the brand is named out loud. Every appearance comes back with its timestamp.

This guide covers the smallest case: one video, one detection, one result. Identify the video with content_id (a video already in Zelf) or content_url (a link to any supported video). Send one or the other, never both.

Every step shows the same call in all six Zelf clients and in cURL. Pick a language and the install line above the snippet switches with it. Each client reads your key from ZELF_API_KEY.

Key concepts

Detection
A search for a list of brands in one video. Detection is asynchronous: you create a detection, then poll it until it finishes.
Placement
One appearance of a brand in a video, with its timestamp.
Identifiers
Alternative names that count as the brand, such as product lines, characters and slogans.
Negatives
Terms that resemble the brand and should not count.
  1. 01Write the brand list
  2. 02Find the video id
  3. 03Create a detection
  4. 04Create a detection from a video link
  5. 05Retrieve the results
  6. 06Read the placements

Write the brand list

List each brand to search for: a name, its identifiers and optional negatives. You send this list with the detection in step 3. Results depend on the list, so spend a few minutes on each brand.

identifiers2 to 10Names that count as the brand when they are visible on screen or spoken. Include the brand name, sub-brands, product lines, hero products, characters and slogans. Example for Nintendo: Nintendo, Switch, Mario, Pokemon, Zelda. Detection can miss a product or character that is not on the list, so use the 10 slots for the ones most likely to appear in videos.
negativesup to 12Terms that look or sound like the brand but are not it, usually common words. Example for Nike: "Jordan (the country)", because "Jordan" is one of its identifiers. Example for Apple: "apple (the fruit)".
namethe brandThe name the brand has in the results. See the limits in step 3.

Find the video id

GET/api/v1/content/content.list

Call content.list to find the video. Each result has an id (prefixed con_). Send it to detection.create in the next step. If you know the post id on the platform, pass it as external_id for an exact match. Skip this step if you send the video link instead.

external_idstringThe post id on the platform. Exact match. A content search needs at least one of search, brand_id, creator_id, creator_username or external_id, and external_id alone is enough.
creator_usernamestringPartial match, case insensitive. Add content_form=VIDEO to list only videos.
results[].idcon_...The video id. Send it to detection.create.
results[].media_durationfloatSeconds. Detection refuses anything over 5400 (90 minutes).
pip install zelf-sdk
import os

from zelf import ZelfAPIClient

client = ZelfAPIClient(api_key=os.environ["ZELF_API_KEY"])

result = client.content.list(
    external_id="Cx12ab34cd",
    content_form="VIDEO",
)
print(result)

Create a detection

POST/api/v1/detection/detection.create

Send a video id and your brand list to detection.create. The response contains the detection's id (prefixed aer_). Use that id to retrieve the results in the next step. Each request covers one video.

content_idcon_...The id of a video already in Zelf. Send this or content_url, never both and never neither.
VideolimitOne per request, up to 90 minutes long. YouTube, TikTok and Instagram videos (not photo posts).
What countsscopeA brand counts when its logo or name is visible or the brand is named out loud. A product with no visible logo or name, and no spoken mention, is not detected.
BrandslimitUp to 200 per request. No two brand names that differ only by punctuation or digits (for example "Brand" and "Brand 2").
Identifierslimit2 to 10 per brand, each at least 2 characters. Include the brand name itself.
NegativeslimitOptional, up to 12 per brand.
pip install zelf-sdk
import os

from zelf import ZelfAPIClient

client = ZelfAPIClient(api_key=os.environ["ZELF_API_KEY"])

result = client.detection.create(
    content_id="con_8Qd2Vx7mLp",
    context={
        "hints": {
            "brands": [{
                "name": "Nike",
                "identifiers": ["Nike", "Jordan", "Air Force 1"],
                "negatives": ["Jordan (the country)"],
            }, {
                "name": "Starbucks",
                "identifiers": ["Starbucks", "Frappuccino", "Pumpkin Spice Latte"],
                "negatives": [],
            }],
        },
    },
)
print(result)

Create a detection from a video link

POST/api/v1/detection/detection.create

Send content_url instead of content_id when the video is not in Zelf yet, or when you only have its link. The rest of the request is the same.

YouTubeacceptedyoutube.com/watch?v=..., youtu.be/..., youtube.com/shorts/...
TikTokacceptedtiktok.com/@handle/video/...
Instagramacceptedinstagram.com/reel/..., instagram.com/p/..., instagram.com/tv/...
Short linksrefusedvm.tiktok.com, vt.tiktok.com and tiktok.com/t links are refused. Open the link and send the full one it lands on.
pip install zelf-sdk
import os

from zelf import ZelfAPIClient

client = ZelfAPIClient(api_key=os.environ["ZELF_API_KEY"])

result = client.detection.create(
    content_url="https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    context={
        "hints": {
            "brands": [{
                "name": "Nike",
                "identifiers": ["Nike", "Jordan", "Air Force 1"],
                "negatives": ["Jordan (the country)"],
            }],
        },
    },
)
print(result)

To run a different brand list on the same video, create a new detection.

Retrieve the results

GET/api/v1/detection/{id}/detection.get

Detection is asynchronous. Poll detection.get with the detection id until status is SUCCESS or FAILED. A finished detection lists each brand with its placements.

SUCCESSfinalThe detection finished. result lists each brand with its placements.
FAILEDfinalThe detection finished without placements. result.brands is an empty list.
pip install zelf-sdk
import os

from zelf import ZelfAPIClient

client = ZelfAPIClient(api_key=os.environ["ZELF_API_KEY"])

result = client.detection.get("aer_6Pz4Jb8n")
print(result)

Read the placements

GET/api/v1/detection/{id}/detection.get

A finished detection has a result with three parts. video: {video_url, platform, duration_s}. summary: {brands_requested, brands_found, placements, confirmed, review}. brands: one entry per requested brand, in the order requested, each with its placements. A brand with found: false was searched for and does not appear in the video.

timestampplacementA range (mm:ss-mm:ss) or a single moment (mm:ss). Past an hour the minutes keep counting, so 69:23 is an hour and nine minutes in.
modalityplacementVisual (logo or product on screen), Text (brand name written on screen) or Audio (brand name spoken).
what_detected and reasoningplacementWhat appears in the video, and why it counts as the brand.
confidenceplacementconfirmed: the brand was visually confirmed in the video. review: detected, but not visually confirmed. Audio placements are always review, because a spoken mention has nothing visual to confirm. Use audio placements as they are. Check Visual and Text placements marked review before relying on them.
video_url_at_timestampplacementFor YouTube, a link that opens the video just before the placement. Null on TikTok and Instagram.
pip install zelf-sdk
import os

from zelf import ZelfAPIClient

client = ZelfAPIClient(api_key=os.environ["ZELF_API_KEY"])

result = client.detection.get("aer_6Pz4Jb8n")
print(result)
Response
{
  "id": "aer_6Pz4Jb8n",
  "status": "SUCCESS",
  "created_at": "2026-10-07T09:44:10Z",
  "updated_at": "2026-10-07T09:52:38Z",
  "context": {
    "external_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "hints": { "brands": [{ "name": "Nike", "identifiers": ["Nike", "Jordan", "Air Force 1"], "negatives": ["Jordan (the country)"] }] }
  },
  "result": {
    "video": {
      "video_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
      "platform": "youtube",
      "duration_s": 742
    },
    "summary": {
      "brands_requested": 2,
      "brands_found": 1,
      "placements": 2,
      "confirmed": 1,
      "review": 1
    },
    "brands": [
      {
        "brand_name": "Nike",
        "found": true,
        "summary": { "placements": 2, "confirmed": 1, "review": 1 },
        "placements": [
          {
            "timestamp": "01:12-01:19",
            "video_url_at_timestamp": "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=67s",
            "modality": "Visual",
            "what_detected": "Nike swoosh on the right shoe during the lacing shot",
            "reasoning": "The swoosh is unobstructed and fills most of the frame.",
            "confidence": "confirmed"
          },
          {
            "timestamp": "04:03",
            "video_url_at_timestamp": "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=238s",
            "modality": "Audio",
            "what_detected": "Says \"these are the new Nikes\"",
            "reasoning": "The brand is named directly in the voiceover.",
            "confidence": "review"
          }
        ]
      },
      { "brand_name": "Starbucks", "found": false, "summary": { "placements": 0, "confirmed": 0, "review": 0 }, "placements": [] }
    ]
  }
}

Summary

StepRequestInputOutput
1GET /api/v1/content/external_id or creator_usernamecontent id (con_...)
2POST /api/v1/detection/content_id or content_url, and the brand listdetection id (aer_...)
3GET /api/v1/detection/{id}/detection idstatus, result
4read resultdetection resultplacements per brand