Deepgram Voice Agent
Give a Deepgram voice agent Deep Search as a client side function. The caller describes the rows they need, the agent reads back the request and the most it can cost, and after the caller says yes, SpecStream returns every match on the live web as structured rows. Source URL and read time on every value.
SpecStream for Deepgram Voice AgentAgent frameworks & buildersWhat you need
- A Deepgram API key, and Python 3.10 or newer with websockets and httpx (
pip install "websockets>=14" httpx). - A SpecStream API key with a total spending limit. New accounts start with $10 of credit, no card. Create an account.
- 1
1. Create a SpecStream API key
In the SpecStream workspace, open API keys and click New key. Name it for the agent and choose Call the APIs under What the key can do: it holds Authorize paid work, which starting a search needs.
Enter a Total spending limit, the most the agent may spend across all its calls, then click Create key. Copy the key from Copy your key now, because it is shown once. Set it as
SPECSTREAM_API_KEYon the server that runs the agent, besideDEEPGRAM_API_KEY. The key stays on your server: the functions run in your code, and Deepgram never sees it. - 2
2. Define the Deep Search functions
Deepgram reads your functions from
agent.think.functionsin theSettingsmessage. A function without anendpointruns client side: Deepgram sends your code aFunctionCallRequest, and your code answers with aFunctionCallResponse.Deep Search takes three functions.
price_deep_searchreads the request and prices it, and spends nothing.start_deep_searchstarts the priced search, and carriesdefer_until_eot: true, so it is never sent while the caller is still talking.read_deep_searchreads the rows found so far. Therowsparameter is the row limit: the JSON schema caps it at 100, and the code below caps it again. Put each block of this guide in one file,deep_search_agent.py, in order:deep_search_agent.py - 3
3. Call SpecStream from each function
Each function is a few calls to the SpecStream REST API.
price_deep_searchsends the request toPOST /v1/parse, which returnsread_back, the request in plain words, andast_hash. It then asksPOST /v1/capacity/quotesfor the price of that many rows. The quote'smaximum_centsis the most the search can cost, and the quote stays open for 30 minutes.start_deep_searchapproves that quote withPOST /v1/capacity/executions. The amount comes from the quote your code kept, never from the model's arguments, and the quote's own ID is the request key, so a repeated call cannot start a second search:deep_search_agent.py - 4
4. Answer each FunctionCallRequest
Each call in
functionshas anid, anameandargumentsas a JSON string. Run the matching function in its own task, so the socket keeps carrying audio, and send back aFunctionCallResponsewith the sameidandnameand the result as a string incontent. A call that comes withthought_signature(some Gemini models send one) gets it back unchanged.A search runs longer than a turn. When it ends,
tell_when_donesends anInjectAgentMessagewithbehaviorset toqueue, so the agent says the table is ready as soon as the current turn ends:deep_search_agent.py - 5
5. Connect and talk to it
Open the socket with your Deepgram key as
Tokenin theAuthorizationheader and sendSettingsfirst. This test types to the agent withInjectUserMessageand printsConversationText, so it runs without a microphone. In your own agent, stream the microphone as Deepgram's guide shows, and dropkeep_aliveandtype_to_agent. WhenFunctionCallCancelledarrives, the matching task stops and sends no response:deep_search_agent.py - 6
6. Run it
Set
DEEPGRAM_API_KEYandSPECSTREAM_API_KEYand runpython deep_search_agent.py. Type the rows you want, with how many. The agent reads back the request and the maximum price. Type yes, and it starts the search. When the table is ready, the agent says so, and asking for the rows makes it callread_deep_search.
What the caller gets
Deep Search turns one request in plain words into rows from the live web: companies, local businesses, people, grants, tenders, papers and other records, each with the facts the caller asked for. Every row meets the conditions in the request, and each value carries the URL of the page it was read on and when it was read. read_deep_search gives the agent the first 10 rows as a Markdown table to speak from. For the full rows, call GET /v1/finds/{id}/rows without format: each row's cells hold the column's label, the value as display, and a provenance with source_url and observed_at. Follow next_cursor for the rows past the first page.
Why the functions run in your code
Deepgram can also call a function on a server you name, when the function carries an endpoint with url, method and headers. Deep Search needs three calls in order and a quote kept between two turns of the conversation, so these functions run client side. Your SpecStream key stays in your process, and the approved amount comes from the quote your code holds.
How spending stays under control
- Pricing spends nothing.
price_deep_searchonly reads the request and returns a quote. - The caller approves the price. The prompt tells the agent to read the maximum aloud and wait for a yes, and
defer_until_eotholdsstart_deep_searchuntil the caller has finished speaking. - The model cannot change the amount.
confirm_max_centscomes from the quote your code kept, and a search never costs more than it. - The key has a ceiling. Its total spending limit caps what the agent can spend across all calls, and a call over it is refused with
403.
If something doesn't work
| What you see | What to do |
|---|---|
price_deep_search returns a status other than runnable | The request needs more detail, such as the place or the facts to bring back. The agent asks the caller and prices it again. |
start_deep_search returns Unknown or used quote | The quote was used, or the process restarted. Price the request again. A quote also expires after 30 minutes. |
An error with 402 | The balance is too low for the approved maximum. Add balance in your SpecStream workspace. |
An error with 403 | The key has no Authorize paid work, or the search would go over its total spending limit. Raise the limit under API keys. |
An error with 401 | The key is unknown, expired or revoked. Create a new key and set it as SPECSTREAM_API_KEY. |
| The agent never answers after a function call | Deepgram waits for a FunctionCallResponse. Check that every call path sends one with the call's id. |
Cost
Pricing a request, reading rows and reading a search's status cost nothing. A search is charged per delivered row: 5¢, 15¢ or $1 a row depending on how much each row needs, plus 5¢ for a person's email and 15¢ for a phone when the request asks for them. Duplicates and rows a page contradicts are not charged, a search that finds nothing costs nothing, and no search costs more than the maximum the caller approved. New accounts start with $10 of credit, no card. The rates are on pricing, and balance and billing covers quotes.
Web search and page reading too
For one fact in the middle of a call, add a fourth function that calls the Search API, GET /v1/web-search?q=, at $5 per 1,000 searches of up to 10 results, with no quote and no approval. To read a page the caller names, the Extract API is $0.80 per 1,000 pages. The REST API reference lists every call above.
Remove
Take the three functions out of agent.think.functions. Then open API keys in your SpecStream workspace and revoke the key, which cuts the agent off at once. Rows already delivered stay in your account. Browse the other tools SpecStream connects to, or write to [email protected] if a step here doesn't match what you see.
More
SpecStream for Deepgram Voice Agent covers what it does in Deepgram Voice Agent on one page. Every other tool has its guide in Connect your tools. You see the most a request can cost before it starts, and the matches are paid from your balance. Billing has the details. For help, email [email protected].