image_search), faces (faces), on-screen text (onscreen_text), document layout (document_layout) — and the platform resolves the implementation internally. Swapping or upgrading the models behind a feature never changes your config, your API calls, or your pricing.
Every feature applies to a modality (the what kind of files question) and is billed in that modality’s natural unit (the how much question):
Clustering and taxonomy enrichment are included on every modality at no additional charge. For rates, tiers, and how usage is billed, see Billing & Pricing.
Discover features
GET /v1/collections/features returns the live feature menu — modalities, units, feature keys, display names, and current rates. No authentication required. This endpoint is the source of truth: studio, the homepage pricing page, and the billing engine all read the same catalog.
kind tells you how it’s priced:
Create a collection with features
Passfeatures: [...] to Create Collection. The platform resolves the pipeline, pins its version, and defaults the input wiring for you:
Both SDKs accept
features on collection create — Python mixpeek>=1.3.29 (client.collections.create(...)) and JavaScript mixpeek>=0.81.28 (client.collections.createCollection({ createCollectionRequest: { ... } })). Set the namespace when you construct the client (Python namespace=, JS namespace:, or the MIXPEEK_NAMESPACE env var) — collection create is namespace-scoped and returns 403 without it. The feature_extractor field is a deprecated alias.Default input wiring
Every organization’s scaffoldeduploads bucket exposes one standard source property per modality — image, video, audio, pdf, content (text), and url (web). A features-based create maps the pipeline’s inputs to those properties automatically, so the collection is runnable with zero wiring.
If your bucket uses different property names, the create returns a teaching 422 naming the fields that exist so you can rename the property — or fall back to explicit input_mappings via the advanced pipeline config.
Errors teach
Validation on the features path is designed for humans and agents guessing their way in — every rejection tells you what to do instead:- Unknown key —
features: ["face"]responds with the full list of valid feature keys (and reminds you aboutcustom:<plugin>). - One pipeline per collection — some feature combinations need separate collections (e.g. a base feature plus a
companion: trueadd-on). The error lists exactly which features group into which collection: create one collection per group over the same source bucket. Add-ons flaggedcompanion: truein discovery always create a companion collection alongside the base. featuresXORfeature_extractor— provide one or the other, never both.- Not yet available — a feature key the platform recognizes but does not serve is rejected explicitly rather than silently ignored.
Custom features (bring your own)
Custom extractors you publish are your vocabulary — they stay explicitly named and are selected ascustom:<plugin_name>:
Full resolution opt-out
By default, Mixpeek normalizes content once at ingest (video to a 720p mezzanine, images capped at ~1568px max edge, audio to 16kHz mono) — originals are always kept untouched in your bucket. If a workload needs extraction on the original resolution (fine print OCR, tiny logos), opt out per collection:full_res_multiplier in the pricing payload).
Chain collections (advanced)
Searching by one thing often produces content worth searching by another — e.g. search video by scenes, then feed those documents into a second collection to search the results by something else. Collection-to-collection pipelines do exactly this: a collection can read another collection’s documents as its source, forming a processing DAG. See Multi-Tier Feature Extraction.Merge sources with different schemas (advanced)
A collection can read several buckets at once. When those buckets have different schemas,source.field_map chooses which fields from each one land in the merged
collection, and what they are called there.
field_map narrows. A source listed in bucket_ids but absent from field_map
passes all of its fields through, so you only name the sources you want to trim.
The same key works for source.type: "collection", mapping collection_ids.
Two sources cannot write the same field
Every target name in afield_map must be unique across the whole map. Create returns
422 when two entries resolve to the same one, whichever sources they belong to and
whatever their types:
field_map key that is not one of the declared bucket_ids or collection_ids is
rejected the same way.
This makes field_map a selector, never a converger. Two sources that name the same
thing differently, headline and title, cannot be folded onto one field here. Land
them as separate fields, or align the names in the source schemas.
Merging without a field_map
Leavefield_map off and every source contributes all of its fields, unioned by name.
That path does converge, and it is the one to reach for when your sources already
agree on names:
Reading a merged collection
The output schema is the union of what every source contributes, so a document carries the fields its own source mapped andnull for the rest. Given the example
above, a document from bkt_video_ads has a title value and display_name of
null, and one from bkt_static_ads has the reverse.
Filter on root_bucket_id to select the documents one source contributed.
A
null here means the source never mapped that field, not that the value was
empty. Set default on an entry when you want a placeholder instead, which fills
only for documents from that source when its source_path is missing.Build a collection from a retriever pipeline (advanced)
A collection normally runs a feature extractor over its source. It can instead run a retriever pipeline over an upstream collection, which makes it a materialized view of that collection’s documents.
Each
write_back_fields entry takes source_field (a dot path into the retriever
result), target_field (the field written on the document), and an optional mode of
first, all_as_array, or concat when a stage returns several results.
A collection is extractor-based or transform-based, never both. feature_extractor
became optional when retriever_transform arrived, and create returns 422 if you send
neither or both.
Run it with POST /v1/collections/{collection_id}/trigger.
How the output is shaped
Cardinality follows the pipeline, and the mechanism is the document id:- Without a
group_bystage, each output document takes its id from the upstream document, so the view is 1:1. - With one, the id comes from the value of the grouped field, so upstream rows
sharing that value collapse into exactly one document. The field is taken from the
last
group_bystage in the pipeline, defaulting tosource_object_id.
group_by orders
groups by score before the collapse. The rest of that group’s rows are not merged into
it; they are dropped.
Ids are deterministic either way, so a re-trigger upserts in place. Re-running after new
upstream documents arrive updates the view incrementally rather than appending a second
copy of it.
A trigger reads the entire source collection, cursor-paging through it. The
100-document cap on POST /v1/documents/list bounds that endpoint, not this, and there
is no paging option to set. A source of 50,000 documents produces a view over all
50,000.
feature_extractor cannot be added to a transform collection afterward. It is a
structural field on every collection, so PATCH rejects it with 422 and tells you
to clone instead. Pick the build mode at create time.Find your feature_uri (to search)
A feature_search stage needs a feature_uri — the exact vector index to search. You picked features by name; each one produces a vector index whose feature_uri looks like mixpeek://<extractor>@<version>/<model>.
The reliable, version-proof way — read it from the collection. After processing, GET the collection and copy the URI; this always matches what you actually built, even if model versions change:
feature_uri (the exact string a fresh @v1 collection produces):
Video transcript search.
video_search produces one blended scene embedding (gemini-embedding-2); the transcript rides along as a text payload field, not a separate searchable vector. To search a transcript as its own vector, add the multimodal_understanding feature with transcription enabled — it creates a second index mixpeek://multimodal_extractor@v1/multilingual_e5_large_instruct_v1. When in doubt, GET the collection and read the feature_uris it actually has.Estimate before you run
POST /v1/organizations/billing/estimate quotes planned ingestion using the same rating engine that bills you — the quote and the charge can’t disagree:
How this relates to extractors
Under the hood every feature resolves to an extraction pipeline, and custom extractors let you register your own. But built-in extractor names are no longer part of the public API surface — they remain accepted in configs only as deprecated aliases. If you have existing configs usingfeature_extractor, see the migration guide; for advanced pipeline knobs (input mappings, field passthrough, parameters), see Pipeline Configuration.
