Want to collect an AI-generated answer together with the sources it cites? SerpApi's Duck AI API, also called the DuckDuckGo AI Chat API, returns answers as structured text blocks and Markdown, with cited sources when Duck AI uses web search.
You can use it to build a small research helper, compare how different models answer a question, or inspect the sources behind an answer. In this tutorial, we'll ask a sample question using cURL, Python, and JavaScript, then choose a model and send a follow-up question.
What data can you retrieve with the Duck AI API? #
The response separates the answer into text_blocks and provides the full formatted answer in markdown. Here are the most useful fields to start with:
- The full answer:
markdowncontains the answer in Markdown, including references when present. - Individual answer blocks:
text_blocksidentifies paragraphs, headings, lists, tables, and code blocks throughtype; paragraph and heading text is stored insnippet. - Cited sources:
referencescontains source records with atitleandlink. - Citation connections: A block's
reference_indexespoints to the corresponding source records by theirindex. - Suggested searches:
related_searchescontains suggestedqueryvalues and aserpapi_linkfor a separateDuckDuckGo Search API request. - Conversation context:
subsequent_request_tokenlets you ask another question with the earlier prompts and answers as context.
Sources and suggested searches depend on the answer; citations do not guarantee that an AI-generated answer is correct.
See the Duck AI API documentation for the complete field and parameter reference.
How to use the Duck AI API #
You can use this API with any programming languages or AI tools.
Try it in the playground
We have a playground where you can play around with any of our APIs and their parameters:
Inspect the answer blocks, the full Markdown answer, and any returned references. The documentation's example contains paragraphs, a list of growing regions, and cited sources. Your returned wording and sources may differ.
Get your API key
Create a SerpApi account, then copy your key from the API key page.
In a macOS or Linux terminal, set the environment variable used by the examples below:
export SERPAPI_API_KEY="YOUR_SERPAPI_API_KEY"
Replace the placeholder locally with your own key. Keep it out of source files, shared screenshots, and public logs.
Make a GET request
Send your prompt to https://serpapi.com/search with engine=duck_ai, q, and api_key. JSON is the default, so you do not need an output parameter:
https://serpapi.com/search?engine=duck_ai&q=where%20does%20coffee%20grow%3F&api_key=YOUR_SERPAPI_API_KEY
For a Markdown response, add output=md to the same request:
https://serpapi.com/search?engine=duck_ai&q=where%20does%20coffee%20grow%3F&api_key=YOUR_SERPAPI_API_KEY&output=md
Markdown output is a curated version of the response. Use JSON when you need exact fields, citation indexes, or a conversation token. There is an important distinction here: the JSON response already contains an answer string called markdown. Setting output=md changes the format of the entire response, rather than adding that field.
cURL implementation
Here is the equivalent GET request using cURL:
curl --get "https://serpapi.com/search" \
--data-urlencode "engine=duck_ai" \
--data-urlencode "q=where does coffee grow?" \
--data-urlencode "api_key=$SERPAPI_API_KEY"
q is your prompt, and engine selects the Duck AI API. --data-urlencode handles spaces and the question mark for you. Check for an error field before using the returned answer.
Python implementation
Use the official SerpApi Python SDK. In your Python virtual environment, install:
pip install serpapi
Create a file named main.py:
import os
import serpapi
client = serpapi.Client(
api_key=os.environ["SERPAPI_API_KEY"],
timeout=60,
)
response = client.search({
"engine": "duck_ai",
"q": "where does coffee grow?",
})
if "error" in response:
raise SystemExit("Duck AI returned an error. Check your key and request in the Playground.")
answer = response.get("markdown")
if not answer:
raise SystemExit("No answer was returned for this prompt.")
print(answer)
for reference in response.get("references", [])[:3]:
print(f"\nSource: {reference['title']}")
print(f"URL: {reference['link']}")
Run it in the terminal where you set your API key:
python main.py
The SDK returns a dictionary-like result, so there is no need to call .json(). This script prints the full Markdown answer and up to three source titles and URLs. Printing markdown preserves the answer's lists, tables, and code formatting without parsing each block separately.
Some answers have no cited sources, so the references loop may have nothing to print even when an answer is returned.
JavaScript implementation
Use a current Node.js LTS release and the official SerpApi JavaScript library:
npm install serpapi
Create a file named main.js:
async function main() {
const apiKey = process.env.SERPAPI_API_KEY;
if (!apiKey) {
console.error("Set SERPAPI_API_KEY before running main.js.");
process.exitCode = 1;
return;
}
const { getJson } = await import("serpapi");
const response = await getJson({
engine: "duck_ai",
q: "where does coffee grow?",
api_key: apiKey,
});
if ("error" in response) {
console.error("Duck AI returned an error. Check your key and request in the Playground.");
process.exitCode = 1;
return;
}
if (!response.markdown) {
console.error("No answer was returned for this prompt.");
process.exitCode = 1;
return;
}
console.log(response.markdown);
for (const reference of (response.references ?? []).slice(0, 3)) {
console.log(`\nSource: ${reference.title}`);
console.log(`URL: ${reference.link}`);
}
}
main().catch(() => {
console.error("The request could not complete. Check your SDK installation, API key, and network connection.");
process.exitCode = 1;
});
Run it in the terminal where you set your API key:
node main.js
Other languages and no-code options
See the other programming-language integrations, SerpApi's Make.com app, and SerpApi's verified n8n node.
For engines without a native operation, the announcements describe Make's Universal "Make an API Call" module and n8n's generic HTTP Request node. Reuse the endpoint and parameters above with the platform's stored SerpApi credentials.
Customize your Duck AI requests #
Interesting parameter in this API:
model: Choose the model answering the prompt. For example,claude-haiku-4-5selects Claude Haiku 4.5. The documented default isgpt-5.6-luna; check the documentation for the current supported list.subsequent_request_token: Pass the token from the preceding response together with a newqto continue that conversation.
For example, ask the same coffee question with Claude Haiku 4.5
curl --get "https://serpapi.com/search" \
--data-urlencode "engine=duck_ai" \
--data-urlencode "q=where does coffee grow?" \
--data-urlencode "model=claude-haiku-4-5" \
--data-urlencode "api_key=$SERPAPI_API_KEY"
Ask a follow-up question
After the first answer, you might ask: "which of those countries produces the most?" This is the follow-up prompt shown in the documentation.
Append the following to the earlier Python example. It reuses that example's client and response, keeping the same default model:
token = response.get("subsequent_request_token")
if not token:
raise SystemExit("No conversation token was returned.")
follow_up = client.search({
"engine": "duck_ai",
"q": "which of those countries produces the most?",
"subsequent_request_token": token,
})
if "error" in follow_up:
raise SystemExit("The follow-up request failed.")
answer = follow_up.get("markdown")
if not answer:
raise SystemExit("No answer was returned for the follow-up prompt.")
print(answer)
This makes one additional API request, which can consume a search credit. For another turn, use the new token from follow_up, not the original token. Treat tokens as private context: they hold the conversation and should not be shared in logs or screenshots.
Warning: Conversations longer than about 16,000 characters across prompts and answers cannot be continued because the request becomes too long. This token continues a conversation; it is not a result-pagination cursor.
Scraping DuckDuckGo search results
SerpApi also offers a dedicated API to fetch search results from the DuckDuckGo website. Learn more about it here:
Conclusion #
You can now retrieve a Duck AI answer, read its cited sources, choose a model, and ask a follow-up question using SerpApi. Try a question relevant to your project in the Duck AI Playground, then use JSON for field-level processing or Markdown for readable answer content.
For related APIs on other AI-answer engines, check out our AI SEO (GEO) API solution.
Frequently asked questions #
Which parameters are required?
A basic request needs engine=duck_ai, your prompt in q, and your SerpApi api_key. Model selection and the conversation token are optional.
Can I choose a different AI model?
Yes. Set model to one of the values in the Duck AI API documentation. The documentation lists claude-haiku-4-5 among the options and gpt-5.6-luna as the default.
Does every answer include sources?
No. Duck AI cites sources when it searches the web to answer. Do not assume that references or related_searches is present for every prompt.
Can I continue the conversation?
Yes. Send a new q with the preceding response's subsequent_request_token. Use the latest returned token for each turn, and keep the documented conversation-length limit in mind.
Should I use JSON or Markdown?
Use JSON to work with exact fields, source records, and continuation tokens. Use output=md when you want the response as readable Markdown. You can also read the answer's markdown string from the default JSON response, as the code examples do.