Guides/Writing a schema

How to write an extraction schema

How to write an extraction schema: describe columns in plain English, pick types like verbatim-string or enum, and avoid pitfalls that empty a column.

Columns

A schema is a list of columns. Each has a name, which becomes the JSON key, a type, and a description, which is what the extraction actually reads. multiple sits beside the list and asks for many rows per video.

{
"multiple": true,
"schema": [
{
"name": "restaurant_name",
"type": "verbatim-string",
"description": "Name of the restaurant, diner or food spot the host visited"
},
{
"name": "food_ordered",
"type": "string[]",
"description": "What food or dish the host said he ate or ordered there"
}
]
}

Descriptions do the work

Write the description as you would explain the column to a person who has not seen the video. Name the thing, then say how it is likely to be spoken about. A bare label gives the model almost nothing to go on, and it is the most common reason a column comes back empty or full of the wrong thing.

Weak

"price"

Better

"The price the host says the item cost, including the currency if they mention it"

For a field with a loose, general name — a category rather than a specific fact — the same rule needs one more piece: say what should not match, since the video will usually mention other things in a similar way. This matters most for repeated-row fields, where a vague description gets tested against the whole video rather than one clear spot.

Weak

"gas stations mentioned"

Better

"The name of a gas station or convenience store brand the host visits — not snacks, drinks, or fuel types mentioned along the way, just the store's own name"

Column types

A column can also declare a type. It changes two things: what the extraction is allowed to answer with, and what you get back in the JSON. Leave it off and the first column is verbatim-string and the rest are string, which is what most schemas want.

These are the type names our specialized extraction model is trained on. It reads a typed template, and the names you write here go into it directly — so what you set and what the model is told are the same word.

verbatim-string

Copied from the transcript word for word. The default for the first column, and what a name, a title or a quote wants.

string

Answered in the model's own words. The default for every column after the first — summaries, explanations, anything paraphrased.

number / integer

Comes back as a number rather than a string. A figure the video never says is left null.

boolean

true or false, for a yes-or-no question about the video.

date

A date the narration mentions.

enum

The answer has to be one of the options you list. Use it for a fixed vocabulary — a sentiment, a verdict, a category — so rows stay comparable across videos.

{
"multiple": true,
"max_rows": 20,
"columns": [
{
"name": "product",
"description": "Name of the product being reviewed",
"type": "verbatim-string"
},
{
"name": "verdict",
"description": "What the host concluded about it",
"type": "string"
},
{
"name": "price",
"description": "Price the host says it costs",
"type": "number"
},
{
"name": "recommendation",
"description": "Whether the host tells viewers to buy it",
"type": "enum",
"options": [
"buy",
"wait",
"skip"
]
}
]
}

verbatim-string and the number types are checked against the transcript: a value that does not appear there is dropped rather than returned. That is the point of them — it is how a name or a figure comes back as something the video actually said, instead of something that sounds right.

Sending a template instead

Those columns compile to a typed JSON template, and that template is what the model is given. If you would rather write the template yourself, send it directly instead of columns — with a descriptions object alongside it, because a template carries types and not meanings. Both forms are stored the same way and run the same job.

{
"schema": {
"template": {
"restaurants": [
{
"restaurant_name": "verbatim-string",
"food_ordered": "string"
}
]
},
"descriptions": {
"restaurant_name": "Name of the restaurant, diner or food spot the host visited",
"food_ordered": "What food or dish the host said he ate or ordered there"
}
}
}
NOTE
Write a description for every field. It is the instructions block the model reads, and a field without one falls back to its column name, which is the single largest accuracy loss available here. Nested objects and nested lists are refused rather than flattened — results are merged row by row across the video, and a nested row cannot be merged.

The schema editor shows the compiled template live while you type, and will take a pasted one and turn it back into columns.

One row or many

Leave multiple off and you get one flat object describing the video as a whole. Set it to true and you get an array, one record per subject the narration covers. Use it when the video walks through several of something and each needs its own row.

NOTE
A saved schema is versioned, and a job records the version it ran. Editing publishes a new version rather than changing the old one — otherwise a finished job would start describing work it never did.
NOTE
When the same real name is spelled two different ways in one video — a caption typo, usually — a repeated-row field already collapses that into a single entry rather than reporting it twice. You do not need to account for that in the description. Two genuinely different names stay two rows, even when they are similar: a video that visits both Lucy's and Mel's gets both.

A wide ask on a long video can return more rows than you want — "every reaction the host gives" can be a row a sentence. Set max_rows alongside multiple to keep the first rows found and drop the rest.

Several values in one column

multiple on the schema decides how many rows a video produces. multiple on a column is different: that column holds a list of values inside each row, and comes back as a real JSON array. Use it when one row naturally owns several of something — every dish ordered at one restaurant.

{
"multiple": true,
"columns": [
{
"name": "restaurant_name",
"description": "Name of the restaurant the host visited"
},
{
"name": "dishes",
"description": "Every dish the host orders there",
"multiple": true
}
]
}

The template form writes the same thing as an array around the type: {"restaurants": [{"restaurant_name": "verbatim-string", "dishes": ["string"]}]}. Each time the restaurant comes up in the video it gets its own row, with the dishes said there, so you can combine or count them however you need.

NOTE
The first column names each row, so it cannot hold several values, and an enum column cannot either. Both are refused with a 400 when you submit.

Pitfalls

Asking for what is shown, not said

Anything only visible on screen is invisible to us. If the narration never says it, no description will retrieve it.

Too many columns at once

A schema can hold 12 columns, but the more it asks at once, the more fields start borrowing from each other. If a wide schema gets muddled, run two narrower ones instead.

Several values crammed into one field

With a single-column array schema we split comma-separated runs back apart automatically. With two or more columns we cannot, because there is no reliable way to know which piece of one column matches which piece of another.

Broad categories pull in near matches

A field described only as "gas stations mentioned" also catches snack brands, fuel types, and other nearby nouns the narration happens to use the same way. Say what does not count, not just what does — one short sentence naming the kind of thing to exclude is usually enough to fix it.

Piling on qualifications makes it worse, not better

A description is not something to keep adding clauses to. Past a certain point, a long list of definitions and exceptions crowds out the instruction that actually matters and the field gets messier, not cleaner. State the one or two things that matter and stop.

Was this page useful?Tell us what was missing