{"schema_version":"1.0","publisher":"Primitive SDKs","canonical_url":"https://test.abhinandan.one/go-semantic-search","markdown_url":"https://test.abhinandan.one/go-semantic-search.md","article":{"id":"b7008f72-3137-41a9-aad5-873eecc3f6da","article_slug":"go-semantic-search","parent_article_slug":null,"parent_article_title":null,"kind":"guide","published_at":"2026-08-11T18:55:01.056916+00:00","keywords":["Client.SemanticSearch","SemanticSearchInput","SemanticSearchResult","semantic_search_enabled","POST /v1/semantic-search","semantic-search host-2"],"meta_description":"Client.SemanticSearch runs ranked semantic, hybrid, or keyword search across received and sent mail and requires the Pro plan's semantic_search_enabled entitlement.","og_image_url":null,"source_file_paths":["sdk-go/client.go","sdk-node/src/api/index.ts","sdk-python/src/primitive/client.py"],"recording_id":null,"replayable":false,"task_name":"Semantic Search (Go SDK)","category":"Go SDK","summary":null,"description":"Run ranked semantic, hybrid, or keyword search across received and sent mail from the Go SDK with Client.SemanticSearch, and read the matched-field excerpts and score breakdown on each result row.","content_kind":"repo_page","content_markdown":"## When to use it\n\nUse `Client.SemanticSearch`, which POSTs to `/v1/semantic-search`, when a meaning-based query across received and sent mail (\"the invoice from last month about the API overage\") is more useful than filtering by sender or date. It returns ranked rows rather than a raw list.\n\nThe call requires the Pro plan and the `semantic_search_enabled` entitlement. Callers without them get an [`APIError`](go-error-handling) with `StatusCode: 403`.\n\n<Note>\n\nThe endpoint lives on the same worker host as `/send-mail`. `Client.SemanticSearch` routes to that host-2 client internally, see [Client and Configuration](go-client-configuration) for the dual-host split, so you never configure this yourself.\n\n</Note>\n\n## Run a search\n\n<Steps>\n\n<Step title=\"Build the client\">\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\n\tprimitive \"github.com/primitivedotdev/sdks/sdk-go\"\n\tprimitiveapi \"github.com/primitivedotdev/sdks/sdk-go/api\"\n)\n\nfunc main() {\n\tclient, err := primitive.NewClient(\"prim_test\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\t_ = client\n}\n```\n\n</Step>\n\n<Step title=\"Call SemanticSearch with a query\">\n\n`SemanticSearch` takes a `*primitiveapi.SemanticSearchInput` and a `context.Context` for [cancellation and timeouts](go-context-timeouts):\n\n```go\nctx := context.Background()\n\nres, err := client.SemanticSearch(ctx, &primitiveapi.SemanticSearchInput{\n\tQuery: \"invoice overage from last month\",\n})\nif err != nil {\n\tlog.Fatal(err)\n}\n\nfor _, row := range res.Data {\n\tfmt.Printf(\"%+v\\n\", row)\n}\nfmt.Println(\"meta:\", res.Meta)\n```\n\n</Step>\n\n<Step title=\"Read the ranked results\">\n\n`SemanticSearch` returns a `SemanticSearchResponse`:\n\n```go\ntype SemanticSearchResponse struct {\n\tData []primitiveapi.SemanticSearchResult\n\tMeta primitiveapi.SemanticSearchMeta\n}\n```\n\n- `Data` is the ranked rows, newest-first as a tiebreak within equal scores. Each row carries the matched fields, a match-centered excerpt, and an additive score breakdown.\n- `Meta.Cursor` is non-null when another page is available.\n\n</Step>\n\n</Steps>\n\n## Expected result\n\nA successful call returns a `SemanticSearchResponse` with `Data` populated and a `nil` error. An empty `Data` slice with a `nil` error means the query matched nothing, not a failure, so check `len(res.Data) == 0` rather than treating it as an error path.\n\n## Handling errors\n\nEvery non-2xx response maps to an `*primitive.APIError`, which you inspect with `errors.As`:\n\n```go\nimport (\n\t\"errors\"\n\t\"log\"\n\n\tprimitive \"github.com/primitivedotdev/sdks/sdk-go\"\n\tprimitiveapi \"github.com/primitivedotdev/sdks/sdk-go/api\"\n)\n\nres, err := client.SemanticSearch(ctx, &primitiveapi.SemanticSearchInput{\n\tQuery: \"invoice overage\",\n})\nif err != nil {\n\tvar apiErr *primitive.APIError\n\tif errors.As(err, &apiErr) {\n\t\tif apiErr.StatusCode == 403 {\n\t\t\tlog.Fatal(\"semantic search requires the Pro plan and semantic_search_enabled\")\n\t\t}\n\t}\n\tlog.Fatal(err)\n}\n```\n\nSee [Error Handling](go-error-handling) for the full `APIError` shape, including `RetryAfter` on a `429`.\n\n<Tip>\n\nPassing a nil request fails before any network call with `request is required`, and calling the method on an unconfigured client fails with `client is not configured`. Always build the client with `NewClient` and pass a non-nil `*primitiveapi.SemanticSearchInput`.\n\n</Tip>\n\n## Next steps\n\n<CardGroup cols={2}>\n\n<Card title=\"Receiving and Verifying Webhooks\" href=\"go-receiving-webhooks\">\n\nNormalize inbound mail into a ReceivedEmail before you search or act on it.\n\n</Card>\n\n<Card title=\"Error Handling\" href=\"go-error-handling\">\n\nLook up every error type SemanticSearch and other calls can return.\n\n</Card>\n\n<Card title=\"Client and Configuration\" href=\"go-client-configuration\">\n\nUnderstand the dual-host client that SemanticSearch routes through.\n\n</Card>\n\n<Card title=\"Context, Timeouts, and Cancellation\" href=\"go-context-timeouts\">\n\nApply a deadline or cancellation signal to a SemanticSearch call.\n\n</Card>\n\n</CardGroup>","canonical_base_url":"https://test.abhinandan.one","seo_indexing_enabled":true,"last_modified":"2026-08-21T18:22:43.359885+00:00","video_url":null,"voiceover_url":null,"tools_used":[],"demonstrated_by":[],"steps":[],"related_links":[],"intro":null,"prerequisites":[],"verification":[],"troubleshooting":[],"suggest_edit_url":"https://github.com/abhi-browzer/primitive-sdks/edit/main/sdk-go/client.go","raise_issue_url":"https://github.com/abhi-browzer/primitive-sdks/issues/new?title=Docs+feedback%3A+Semantic+Search+%28Go+SDK%29&body=Page%3A+https%3A%2F%2Ftest.abhinandan.one%2Fgo-semantic-search","page_feedback_enabled":true,"verified_ref":null,"verified_at":"2026-08-11T18:38:45.205849+00:00"}}