Run an AI analysis on a search term
Size the set, start the run, poll it, read the result.
An AI analysis reads the posts your content_filters match and reports on a topic: sentiment, price, quality, competitors and products. You name the topic and hand it the same filters you would send to content.list.
It is a background job. Create it, poll it, then download the result from a signed link. A run can take an hour or more, so poll at a calm interval.
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.
- 01Count what you are about to analyse
- 02Start the analysis
- 03Wait for it to finish
- 04Download the result
- 05See the posts it used
Count what you are about to analyse
The statistics endpoint takes every content filter and returns one row of totals for the whole match set. Run it first: post_count is the only place that count exists, and it tells you whether the filters are too wide or too narrow before you spend a run on them.
post_countinteger | Posts matching the filters. The number the analysis will draw from. |
|---|---|
unique_creator_countinteger | How many creators those posts come from. |
total_views, total_engagementinteger | Totals over the whole set. total_earned_media_value_cents and total_media_duration come back too. |
pip install zelf-sdkimport os
from zelf import ZelfAPIClient
client = ZelfAPIClient(api_key=os.environ["ZELF_API_KEY"])
result = client.content.statistics(
search="running shoes",
posted_within_days=90,
content_form="VIDEO",
)
print(result)An analysis reads up to 500 posts, or the limit configured for your workspace. If post_count is far above that, tighten the window or add a view_count_min so the run sees the posts you care about.
Start the analysis
The same filters go in the body as JSON under content_filters, with one difference: platform, language and order_by are arrays here rather than comma separated strings. topic names what the report is about.
namestring, required | Your label for the run. It comes back on every read. |
|---|---|
topicstring, required | What the report is about: a brand, product, person or event, in a few words. For example Running shoes. Not a question: every run covers sentiment, price, quality, competitors and products anyway. Narrow the posts with content_filters. |
analysis_scopearray, optional | CONTENT, COMMENT, or both. Defaults to ["CONTENT"]. Each scope becomes one run with its own status and result. |
content_filtersobject, required | Must be scoped, same rule as a search: one of search, creator_id, creator_username, brand_id or external_id. |
pip install zelf-sdkimport os
from zelf import ZelfAPIClient
client = ZelfAPIClient(api_key=os.environ["ZELF_API_KEY"])
result = client.ai_analysis.create(
name="Running shoes, Q3",
topic="Running shoes",
analysis_scope=["CONTENT"],
content_filters={
"search": "running shoes",
"posted_within_days": 90,
"content_form": "VIDEO",
"platform": ["instagram", "tiktok"],
"language": ["en"],
"view_count_min": 10000,
"order_by": ["view_count"],
},
)
print(result){
"id": "anl_7Km3Qx9pLt",
"analysis_type": "AI_ANALYSIS",
"name": "Running shoes, Q3",
"topic": "Running shoes",
"created_at": "2026-10-07T09:20:03Z",
"modified_at": "2026-10-07T09:20:03Z",
"analyses": [
{ "id": "anr_2Bq8Zt4v", "scope": "CONTENT", "status": "IN_PROGRESS" }
]
}Wait for it to finish
Read the analysis back by id. Each entry under analyses carries its own status. Poll every minute or two, not every second: the ai_analysis quota is 10 requests a second and 1000 an hour, and a tight loop will spend it on nothing.
IN_PROGRESSnot final | Still running. Keep polling. |
|---|---|
COMPLETEDfinal | Finished with a result to download. |
EMPTY_RESULTfinal | Finished, but the filters matched nothing worth analysing. Widen the window or loosen the filters. |
FAILEDfinal | Finished without a result. |
pip install zelf-sdkimport os
from zelf import ZelfAPIClient
client = ZelfAPIClient(api_key=os.environ["ZELF_API_KEY"])
result = client.ai_analysis.list(
id="anl_7Km3Qx9pLt",
)
print(result)Polling is the simple option, not the best one. Subscribe to the ai_analysis.completed webhook and Zelf tells you the moment a run lands, then you fetch the result once.
Download the result
The result is not inline. Each finished run carries a signed link that expires, so fetch it when you are ready to read it rather than storing the link. Ask by analysis_id for every run, or by id for one.
analysis_idstring | Returns every run under the analysis. Send this or id, not both. |
|---|---|
idstring | Returns one run. |
result.download_urlstring or null | Signed link to the payload. Null until the run has produced one. |
result.expires_atinteger | Unix seconds. Re-read the endpoint for a fresh link after it passes. |
pip install zelf-sdkimport os
from zelf import ZelfAPIClient
client = ZelfAPIClient(api_key=os.environ["ZELF_API_KEY"])
result = client.ai_analysis.results(
analysis_id="anl_7Km3Qx9pLt",
)
print(result){
"results": [
{
"id": "anr_2Bq8Zt4v",
"analysis_id": "anl_7Km3Qx9pLt",
"scope": "CONTENT",
"status": "COMPLETED",
"created_at": "2026-10-07T09:20:03Z",
"modified_at": "2026-10-07T10:48:51Z",
"result": {
"download_url": "https://storage.googleapis.com/...",
"expires_at": 1791455331,
"format": "json"
}
}
],
"item_count": 1,
"page": 1,
"next": null,
"previous": null
}See the posts it used
When an answer looks wrong, the usual cause is the input rather than the analysis. These two read back exactly the posts the run drew from, so you can check the filters did what you meant. The contents endpoint pages like any other list.
pip install zelf-sdkimport os
from zelf import ZelfAPIClient
client = ZelfAPIClient(api_key=os.environ["ZELF_API_KEY"])
result = client.ai_analysis.contents(
analysis_id="anl_7Km3Qx9pLt",
page=1,
)
print(result)Summary
| Step | Request | Input | Output |
|---|---|---|---|
| 1 | GET /api/v1/content/statistics/ | content filters | post_count |
| 2 | POST /api/v1/ai_analysis/ | name, topic, content_filters | analysis id (anl_...) |
| 3 | GET /api/v1/ai_analysis/?id= | analysis id | status per run |
| 4 | GET /api/v1/ai_analysis/results/ | analysis id | signed download_url |
| 5 | GET /api/v1/ai_analysis/contents/ | analysis id | the posts it read |