Web search
Web search (웹 검색)
웹 검색은 모델이 인터넷의 최신 정보에 접근하고 출처 인용(sourced citations)이 있는 답변을 제공하게 해줘요. 이를 활성화하려면 Responses API에서 웹 검색 도구를 사용하거나, 경우에 따라 Chat Completions에서 사용해요.
OpenAI 모델에서 사용할 수 있는 웹 검색에는 세 가지 주요 유형이 있어요.
- 비추론 웹 검색: 비추론 모델이 사용자 쿼리를 웹 검색 도구로 보내고, 도구가 최상위 결과를 바탕으로 응답을 반환해요. 내부 계획이 없고 모델은 단순히 검색 도구의 응답을 전달해요. 이 방식은 빠르고 빠른 조회에 이상적이에요.
- 추론 모델의 에이전틱 검색(agentic search): 모델이 검색 과정을 능동적으로 관리하는 접근 방식이에요. 사고 체인(chain of thought)의 일부로 웹 검색을 수행하고, 결과를 분석하며, 계속 검색할지 결정할 수 있어요. 이 유연성 덕분에 에이전틱 검색은 복잡한 워크플로에 잘 맞지만, 검색이 빠른 조회보다 오래 걸린다는 뜻이기도 해요. 예를 들어
gpt-5.5같은 모델에서 reasoning 수준을 조정해 검색의 깊이와 지연 시간을 모두 바꿀 수 있어요. - 딥 리서치(deep research): 추론 모델이 깊이 있고 확장된 조사를 수행하는 특수한 에이전트 기반 방식이에요. 모델은 사고 체인의 일부로 웹 검색을 수행하며, 종종 수백 개의 소스에 접근해요. 딥 리서치는 몇 분 동안 실행될 수 있고 background 모드와 함께 쓰는 것이 가장 좋아요.
gpt-5.5를 reasoninghigh또는xhigh로 설정해서 사용하세요.
출처: 문서
본문
통합 선택
| 사용 사례 | 권장 경로 | 참고 |
|---|---|---|
| 새 웹 검색 통합 | Responses API + web_search + gpt-5.5 |
필터, 소스, 실시간 접근 통제, 더 긴 리서치 실행 같은 호스티드 웹 검색 통제를 지원해요 |
| 기존 Chat Completions 검색 통합 | Chat Completions + gpt-5-search-api |
Chat Completions 통합을 보존해야 할 때만 사용하세요 |
| 다단계 리서치 또는 장기 실행 보고 | reasoning high 또는 xhigh인 gpt-5.5 |
몇 분이 걸릴 수 있는 보고서에는 background 모드를 사용하세요 |
Responses API를 사용하면 콘텐츠를 생성하는 API 요청의 tools 배열에 웹 검색을 구성해 활성화할 수 있어요. 다른 도구와 마찬가지로, 모델은 입력 프롬프트의 콘텐츠에 따라 웹을 검색할지 여부를 선택할 수 있어요.
새 Responses API 통합에는 { "type": "web_search" }를 사용하세요. 이전의 web_search_preview 도구는 레거시 통합에서 계속 사용할 수 있지만, filters, external_web_access, return_token_budget 같은 새로운 통제는 지원하지 않아요.
웹 검색 도구 예시
import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-6-astra",
tools: [{ type: "web_search" }],
input: "What was a positive news story from today?",
});
console.log(response.output_text);
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
tools=[{"type": "web_search"}],
input="What was a positive news story from today?",
)
print(response.output_text)
package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Tools: []responses.ToolUnionParam{
responses.ToolParamOfWebSearch(responses.WebSearchToolTypeWebSearch),
},
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("What was a positive news story from today?")},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.WebSearchTool;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("What was a positive news story from today?")
.addTool(WebSearchTool.builder().type(WebSearchTool.Type.WEB_SEARCH).build())
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
using OpenAI.Responses;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
ResponsesClient client = new(key);
CreateResponseOptions options = new() { Model = "gpt-6-astra" };
options.Tools.Add(ResponseTool.CreateWebSearchTool());
options.InputItems.Add(
ResponseItem.CreateUserMessageItem("What was a positive news story from today?")
);
ResponseResult response = await client.CreateResponseAsync(options);
Console.WriteLine(response.GetOutputText());
require "openai"
openai = OpenAI::Client.new
response = openai.responses.create(
model: "gpt-6-astra",
tools: [{ type: "web_search" }],
input: "What was a positive news story from today?"
)
puts(response.output_text)
curl "https://api.openai.com/v1/responses" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{
"model": "gpt-6-astra",
"tools": [{"type": "web_search"}],
"input": "what was a positive news story from today?"
}'
openai responses create \
--model gpt-6-astra \
--raw-output \
--transform 'output.#(type=="message").content.0.text' <<'YAML'
tools:
- type: web_search
input: What was a positive news story from today?
YAML
출력과 인용
웹 검색 도구를 사용하는 모델 응답은 두 부분을 포함해요.
web_search_call출력 항목 — 검색 호출의 ID와web_search_call.action에 담긴 수행된 액션. 액션은 다음 중 하나예요.search— 웹 검색을 나타내요. 보통(항상은 아니지만) 검색된queries를 포함해요. 검색 액션은 도구 호출 비용이 발생해요 (pricing 참고).open_page— 열리는 페이지를 나타내요. 추론 모델에서 지원돼요.find_in_page— 페이지 내 검색을 나타내요. 추론 모델에서 지원돼요.
message출력 항목 — 다음을 포함해요.message.content[0].text의 텍스트 결과- 인용된 URL에 대한 annotation
message.content[0].annotations
기본적으로 모델 응답에는 웹 검색 결과에서 찾은 URL에 대한 인라인 인용이 포함돼요. 추가로 url_citation annotation 객체가 인용된 소스의 URL, 제목, 위치를 담고 있어요.
웹 결과나 웹 결과에 포함된 정보를 최종 사용자에게 표시할 때는 인라인 인용을 사용자 인터페이스에서 명확하게 보이게 하고 클릭 가능하게 만들어야 해요.
[
{
"type": "web_search_call",
"id": "ws_67c9fa0502748190b7dd390736892e100be649c1a5ff9609",
"status": "completed",
"action": {
"type": "search",
"query": "latest news about AI"
}
},
{
"id": "msg_67c9fa077e288190af08fdffda2e34f20be649c1a5ff9609",
"type": "message",
"status": "completed",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "On March 6, 2025, several news...",
"annotations": [
{
"type": "url_citation",
"start_index": 2606,
"end_index": 2758,
"url": "https://...",
"title": "Title..."
}
]
}
]
}
]
레거시 웹 검색에서 마이그레이션
| 현재 사용 중 | 권장 경로 | 참고 |
|---|---|---|
Responses의 web_search_preview |
web_search로 마이그레이션 |
web_search는 filters, external_web_access, return_token_budget 같은 새 통제를 지원해요 |
gpt-4o-search-preview 또는 gpt-4o-mini-search-preview |
Responses web_search로 마이그레이션, 또는 Chat Completions에 머물러야 한다면 gpt-5-search-api 사용 |
프리뷰 검색 모델은 2026-07-23에 폐기·종료됐어요 |
| Chat Completions 검색 통합 | gpt-5-search-api 사용, 또는 더 많은 도구 통제·선택적 검색을 위해 Responses web_search로 마이그레이션 |
Chat Completions 검색 모델은 응답 전에 항상 검색하고, Responses 검색은 도구예요 |
검색 컨텍스트 크기
search_context_size는 모델이 응답을 생성하기 전에 웹 검색 결과에서 얼마나 많은 컨텍스트를 사용할 수 있는지 제어해요. 간단한 조회에는 low, 균형 잡힌 기본값에는 medium, 답변이 검색 결과에서 더 많은 세부 사항을 요구할 때는 high를 사용하세요. 이 설정은 정확한 토큰 수를 정하거나 특정 수의 소스·인용을 보장하지 않아요.
검색 컨텍스트 크기 설정
import OpenAI from "openai";
const openai = new OpenAI();
const response = await openai.responses.create({
model: "gpt-6-astra",
tools: [
{
type: "web_search",
search_context_size: "low",
},
],
input: "What movie won best picture in 2025?",
});
console.log(response.output_text);
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
tools=[
{
"type": "web_search",
"search_context_size": "low",
}
],
input="What movie won best picture in 2025?",
)
print(response.output_text)
package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
tool := responses.ToolParamOfWebSearch(responses.WebSearchToolTypeWebSearch)
tool.OfWebSearch.SearchContextSize = responses.WebSearchToolSearchContextSizeLow
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Tools: []responses.ToolUnionParam{tool},
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("What movie won best picture in 2025?")},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.WebSearchTool;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("What movie won best picture in 2025?")
.addTool(
WebSearchTool.builder()
.type(WebSearchTool.Type.WEB_SEARCH)
.searchContextSize(WebSearchTool.SearchContextSize.LOW)
.build())
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
using OpenAI.Responses;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
ResponsesClient client = new(key);
CreateResponseOptions options = new() { Model = "gpt-6-astra" };
options.Tools.Add(
ResponseTool.CreateWebSearchTool(
searchContextSize: WebSearchToolContextSize.Low
)
);
options.InputItems.Add(
ResponseItem.CreateUserMessageItem("What movie won best picture in 2025?")
);
ResponseResult response = await client.CreateResponseAsync(options);
Console.WriteLine(response.GetOutputText());
require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
input: "What movie won best picture in 2025?",
tools: [
{
type: :web_search,
search_context_size: :low
}
]
)
puts(response.output_text)
curl "https://api.openai.com/v1/responses" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{
"model": "gpt-6-astra",
"tools": [{
"type": "web_search",
"search_context_size": "low"
}],
"input": "What movie won best picture in 2025?"
}'
더 긴 웹 리서치 실행
return_token_budget은 GPT-5+ 추론 모델과의 Responses API 검색 실행 중에 도구가 반환할 수 있는 웹 검색 결과 콘텐츠 양을 제어해요. 대부분의 요청에서는 기본값을 유지하세요. 많은 페이지를 검사해야 하고 그 외에는 표준 반환 토큰 상한에서 멈출 수 있는 고집약 리서치나 평가 실행에서만 unlimited로 설정하세요.
unlimited는 지연 시간과 비용을 늘릴 수 있으므로 선택적으로 사용하세요. 장기 실행 다중 검색 작업에는 background 모드(background: true)를 사용해 요청이 비동기로 계속 실행되고 나중에 최종 응답을 검색할 수 있게 하세요.
| 값 | 동작 |
|---|---|
default |
웹 검색 결과에 표준 반환 토큰 예산을 사용해요. return_token_budget을 생략한 것과 같은 동작이에요. |
unlimited |
웹 검색 실행의 기본 반환 토큰 예산을 제거해요. |
이 파라미터는 GPT-5+ 추론 웹 검색과 함께하는 호스티드 Responses API web_search 도구에만 적용돼요. 검색 컨텍스트 창은 바꾸지 않으며, 비추론 웹 검색, 레거시 Search API 경로, 컨테이너 웹 검색, Chat Completions 검색 모델, web_search_preview에는 적용되지 않아요. 지원되는 값은 default와 unlimited뿐이며, null, 숫자, 다른 문자열은 거부돼요.
더 긴 웹 검색 실행
import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-6-astra",
reasoning: { effort: "xhigh" },
tools: [
{
type: "web_search",
return_token_budget: "unlimited",
},
],
input: [
"Research the economic impact of semaglutide on global healthcare systems.",
"",
"Do:",
"- Include specific figures, trends, statistics, and measurable outcomes.",
"- Prioritize reliable, up-to-date sources: peer-reviewed research, health organizations (e.g., WHO, CDC), regulatory agencies, or pharmaceutical earnings reports.",
"- Include inline citations and return all source metadata.",
"",
"Be analytical, avoid generalities, and ensure that each section supports data-backed reasoning that could inform healthcare policy or financial modeling.",
].join("\n"),
});
console.log(response.output_text);
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
reasoning={"effort": "xhigh"},
tools=[
{
"type": "web_search",
"return_token_budget": "unlimited",
}
],
input="""Research the economic impact of semaglutide on global healthcare systems.
Do:
- Include specific figures, trends, statistics, and measurable outcomes.
- Prioritize reliable, up-to-date sources: peer-reviewed research, health organizations (e.g., WHO, CDC), regulatory agencies, or pharmaceutical earnings reports.
- Include inline citations and return all source metadata.
Be analytical, avoid generalities, and ensure that each section supports data-backed reasoning that could inform healthcare policy or financial modeling.""",
)
print(response.output_text)
package main
import (
"context"
"fmt"
"strings"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
"github.com/openai/openai-go/v3/shared"
)
func main() {
client := openai.NewClient()
tool := responses.ToolParamOfWebSearch(responses.WebSearchToolTypeWebSearch)
tool.OfWebSearch.SetExtraFields(map[string]any{"return_token_budget": "unlimited"})
input := strings.Join([]string{
"Research the economic impact of semaglutide on global healthcare systems.",
"",
"Do:",
"- Include specific figures, trends, statistics, and measurable outcomes.",
"- Prioritize reliable, up-to-date sources: peer-reviewed research, health organizations, regulatory agencies, or pharmaceutical earnings reports.",
"- Include inline citations and return all source metadata.",
"",
"Be analytical, avoid generalities, and ensure that each section supports data-backed reasoning that could inform healthcare policy or financial modeling.",
}, "\n")
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Reasoning: shared.ReasoningParam{Effort: shared.ReasoningEffortXhigh},
Tools: []responses.ToolUnionParam{tool},
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String(input)},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.JsonValue;
import com.openai.models.Reasoning;
import com.openai.models.ReasoningEffort;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.WebSearchTool;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input(
"Research the economic impact of semaglutide on global healthcare systems. Include current figures and citations.")
.reasoning(Reasoning.builder().effort(ReasoningEffort.XHIGH).build())
.addTool(
WebSearchTool.builder()
.type(WebSearchTool.Type.WEB_SEARCH)
.putAdditionalProperty("return_token_budget", JsonValue.from("unlimited"))
.build())
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
input: "Research the economic impact of semaglutide on global healthcare systems. Include current figures and citations.",
reasoning: { effort: :xhigh },
tools: [
{
type: :web_search,
return_token_budget: :unlimited
}
]
)
puts(response.output_text)
curl "https://api.openai.com/v1/responses" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{
"model": "gpt-6-astra",
"reasoning": { "effort": "xhigh" },
"tools": [
{
"type": "web_search",
"return_token_budget": "unlimited"
}
],
"input": "Research the economic impact of semaglutide on global healthcare systems.\n\nDo:\n- Include specific figures, trends, statistics, and measurable outcomes.\n- Prioritize reliable, up-to-date sources: peer-reviewed research, health organizations (e.g., WHO, CDC), regulatory agencies, or pharmaceutical earnings reports.\n- Include inline citations and return all source metadata.\n\nBe analytical, avoid generalities, and ensure that each section supports data-backed reasoning that could inform healthcare policy or financial modeling."
}'
도메인 필터링
웹 검색의 도메인 필터링은 결과를 특정 도메인 집합으로 제한할 수 있게 해줘요. filters 파라미터로 최대 100개의 allowed_domains 또는 100개의 blocked_domains를 구성할 수 있어요. 도메인을 서식 지정할 때는 HTTP 또는 HTTPS 접두사를 생략하세요. 예를 들어 https://openai.com/ 대신 openai.com을 사용해요. 이 방식은 검색에 서브도메인도 포함해요. 도메인 필터링은 web_search 도구와 함께하는 Responses API에서만 사용할 수 있어요.
소스 (Sources)
웹 검색 중에 가져온 모든 URL을 보려면 sources 필드를 사용하세요. 가장 관련성 높은 참조만 보여주는 인라인 인용과 달리, sources는 모델이 응답을 만들 때 참고한 URL의 완전한 목록을 반환해요.
소스 수는 인용 수보다 많은 경우가 많아요. 실시간 타사 피드도 여기 표시되며 oai-sports, oai-weather, oai-finance로 표시돼요. sources 필드는 web_search와 web_search_preview 도구 모두에서 사용할 수 있어요.
소스 나열
import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-6-astra",
reasoning: { effort: "low" },
tools: [
{
type: "web_search",
filters: {
allowed_domains: [
"pubmed.ncbi.nlm.nih.gov",
"clinicaltrials.gov",
"www.who.int",
"www.cdc.gov",
"www.fda.gov",
],
blocked_domains: ["reddit.com", "quora.com", "wikipedia.org"],
},
},
],
tool_choice: "auto",
include: ["web_search_call.action.sources"],
input:
"Please perform a web search on how semaglutide is used in the treatment of diabetes.",
});
console.log(response.output_text);
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
reasoning={"effort": "low"},
tools=[
{
"type": "web_search",
"filters": {
"allowed_domains": [
"pubmed.ncbi.nlm.nih.gov",
"clinicaltrials.gov",
"www.who.int",
"www.cdc.gov",
"www.fda.gov",
],
"blocked_domains": [
"reddit.com",
"quora.com",
"wikipedia.org",
],
},
}
],
tool_choice="auto",
include=["web_search_call.action.sources"],
input="Please perform a web search on how semaglutide is used in the treatment of diabetes.",
)
print(response.output_text)
package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
"github.com/openai/openai-go/v3/shared"
)
func main() {
client := openai.NewClient()
tool := responses.ToolParamOfWebSearch(responses.WebSearchToolTypeWebSearch)
tool.OfWebSearch.Filters = responses.WebSearchToolFiltersParam{
AllowedDomains: []string{"pubmed.ncbi.nlm.nih.gov", "clinicaltrials.gov", "www.who.int", "www.cdc.gov", "www.fda.gov"},
}
tool.OfWebSearch.Filters.SetExtraFields(map[string]any{"blocked_domains": []string{"reddit.com", "quora.com", "wikipedia.org"}})
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Reasoning: shared.ReasoningParam{Effort: shared.ReasoningEffortLow},
Tools: []responses.ToolUnionParam{tool},
Include: []responses.ResponseIncludable{responses.ResponseIncludableWebSearchCallActionSources},
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Please perform a web search on how semaglutide is used in the treatment of diabetes.")},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.JsonValue;
import com.openai.models.Reasoning;
import com.openai.models.ReasoningEffort;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ResponseIncludable;
import com.openai.models.responses.WebSearchTool;
import java.util.List;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("Search for how semaglutide is used in the treatment of diabetes.")
.reasoning(Reasoning.builder().effort(ReasoningEffort.LOW).build())
.addInclude(ResponseIncludable.of("web_search_call.action.sources"))
.addTool(
WebSearchTool.builder()
.type(WebSearchTool.Type.WEB_SEARCH)
.filters(
WebSearchTool.Filters.builder()
.allowedDomains(
List.of(
"pubmed.ncbi.nlm.nih.gov",
"clinicaltrials.gov",
"www.who.int",
"www.cdc.gov",
"www.fda.gov"))
.putAdditionalProperty(
"blocked_domains",
JsonValue.from(List.of("reddit.com", "quora.com", "wikipedia.org")))
.build())
.build())
.build();
var response = client.responses().create(params);
response.output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
response.output().stream()
.flatMap(item -> item.webSearchCall().stream())
.flatMap(call -> call.action().search().stream())
.flatMap(action -> action.sources().stream())
.flatMap(List::stream)
.forEach(source -> System.out.println(source.url()));
require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
reasoning: { effort: :low },
input: "Search for how semaglutide is used in the treatment of diabetes.",
include: ["web_search_call.action.sources"],
tools: [
{
type: :web_search,
filters: {
allowed_domains: [
"pubmed.ncbi.nlm.nih.gov",
"clinicaltrials.gov",
"www.who.int",
"www.cdc.gov",
"www.fda.gov"
],
blocked_domains: ["reddit.com", "quora.com", "wikipedia.org"]
}
}
]
)
puts(response.output_text)
response.output
.grep(OpenAI::Models::Responses::ResponseFunctionWebSearch)
.each do |search_call|
action = search_call.action
next unless action.is_a?(
OpenAI::Models::Responses::ResponseFunctionWebSearch::Action::Search
)
Array(action.sources).each { |source| puts(source.url) }
end
curl "https://api.openai.com/v1/responses" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{
"model": "gpt-6-astra",
"reasoning": { "effort": "low" },
"tools": [
{
"type": "web_search",
"filters": {
"allowed_domains": [
"pubmed.ncbi.nlm.nih.gov",
"clinicaltrials.gov",
"www.who.int",
"www.cdc.gov",
"www.fda.gov"
],
"blocked_domains": [
"reddit.com",
"quora.com",
"wikipedia.org"
]
}
}
],
"tool_choice": "auto",
"include": ["web_search_call.action.sources"],
"input": "Please perform a web search on how semaglutide is used in the treatment of diabetes."
}'
이미지 검색 결과
웹 검색은 일반 텍스트 결과와 함께 이미지 결과를 반환할 수 있어요. 애플리케이션이 제품 사진, 랜드마크, 장소, 이벤트, 시각적 참조 같은 최신 또는 웹 기반의 시각 자료가 필요할 때 이미지 검색을 사용하세요.
이미지 검색을 사용하려면 search_content_types를 image를 포함하도록 설정하세요. 모델이 가져온 이미지를 요약·순위·설명하는 데 도움이 되는 보조 텍스트 결과도 원한다면 text를 추가하세요.
image_settings로 이미지별 동작을 제어하세요.
max_results: 양수의 이미지 결과 수를 요청해요.caption: 가능할 때 짧은 이미지 설명을 요청해요.
원시 이미지 결과를 검사하려면 요청에 web_search_call.results를 포함하고 응답에서 web_search_call.results[]를 읽으세요. 이미지 결과는 어시스턴트 메시지와 별도로 반환되므로, 애플리케이션이 URL이나 메타데이터가 필요할 때 web_search_call 항목을 직접 파싱하세요.
이미지 검색
import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-6-astra",
reasoning: { effort: "low" },
tools: [
{
type: "web_search",
search_content_types: ["image", "text"],
image_settings: {
max_results: 3,
caption: true,
},
},
],
include: ["web_search_call.results"],
input:
"Search for recent images and supporting text sources about the Golden Gate Bridge at sunset.",
});
console.log(response.output);
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
reasoning={"effort": "low"},
tools=[
{
"type": "web_search",
"search_content_types": ["image", "text"],
"image_settings": {
"max_results": 3,
"caption": True,
},
}
],
include=["web_search_call.results"],
input="Search for recent images and supporting text sources about the Golden Gate Bridge at sunset.",
)
print(response.output)
package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
"github.com/openai/openai-go/v3/shared"
)
func main() {
client := openai.NewClient()
tool := responses.ToolParamOfWebSearch(responses.WebSearchToolTypeWebSearch)
tool.OfWebSearch.SetExtraFields(map[string]any{
"search_content_types": []string{"image", "text"},
"image_settings": map[string]any{"max_results": 3, "caption": true},
})
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Reasoning: shared.ReasoningParam{Effort: shared.ReasoningEffortLow},
Tools: []responses.ToolUnionParam{tool},
Include: []responses.ResponseIncludable{responses.ResponseIncludableWebSearchCallResults},
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Search for recent images and supporting text sources about the Golden Gate Bridge at sunset.")},
})
if err != nil {
panic(err)
}
fmt.Println(response.Output)
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.JsonValue;
import com.openai.models.Reasoning;
import com.openai.models.ReasoningEffort;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ResponseIncludable;
import com.openai.models.responses.WebSearchTool;
import java.util.List;
import java.util.Map;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input(
"Search for recent images and supporting text sources about the Golden Gate Bridge at sunset.")
.reasoning(Reasoning.builder().effort(ReasoningEffort.LOW).build())
.addInclude(ResponseIncludable.of("web_search_call.results"))
.addTool(
WebSearchTool.builder()
.type(WebSearchTool.Type.WEB_SEARCH)
.putAdditionalProperty(
"search_content_types", JsonValue.from(List.of("image", "text")))
.putAdditionalProperty(
"image_settings", JsonValue.from(Map.of("max_results", 3, "caption", true)))
.build())
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.webSearchCall().stream())
.map(call -> call._additionalProperties().get("results"))
.filter(java.util.Objects::nonNull)
.forEach(System.out::println);
require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
reasoning: { effort: :low },
input: "Search for recent images and supporting text sources about the Golden Gate Bridge at sunset.",
include: ["web_search_call.results"],
tools: [
{
type: :web_search,
search_content_types: ["image", "text"],
image_settings: {
max_results: 3,
caption: true
}
}
]
)
puts(response.output)
curl "https://api.openai.com/v1/responses" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{
"model": "gpt-6-astra",
"reasoning": { "effort": "low" },
"tools": [
{
"type": "web_search",
"search_content_types": ["image", "text"],
"image_settings": {
"max_results": 3,
"caption": true
}
}
],
"include": ["web_search_call.results"],
"input": "Search for recent images and supporting text sources about the Golden Gate Bridge at sunset."
}'
각 image_result에는 다음이 포함돼요.
image_url: 결과의 표준 이미지 URLsource_website_url: 이미지가 발견된 페이지thumbnail_url: 가능한 경우 썸네일 URLcaption: 가능한 경우 짧은 캡션 또는 설명
{
"output": [
{
"type": "web_search_call",
"status": "completed",
"results": [
{
"type": "image_result",
"image_url": "https://cdn.example/golden-gate-sunset.jpg",
"thumbnail_url": "https://cdn.example/golden-gate-sunset-thumb.jpg",
"source_website_url": "https://example.com/source-page",
"caption": "Golden Gate Bridge at sunset"
}
]
}
]
}
사용자 위치
지리적 기반으로 검색 결과를 다듬으려면 country, city, region, timezone을 사용해 대략적인 사용자 위치를 지정할 수 있어요.
city와region필드는 각각Minneapolis와Minnesota같은 자유 텍스트 문자열이에요.country필드는US같은 두 글자 ISO 국가 코드예요.timezone필드는America/Chicago같은 IANA 타임존이에요.
웹 검색을 사용하는 딥 리서치 모델에서는 사용자 위치가 지원되지 않는다는 점에 유의하세요.
사용자 위치 커스터마이징
import OpenAI from "openai";
const openai = new OpenAI();
const response = await openai.responses.create({
model: "gpt-6-astra",
tools: [
{
type: "web_search",
user_location: {
type: "approximate",
country: "GB",
city: "London",
region: "London",
},
},
],
input: "What are the best restaurants near me?",
});
console.log(response.output_text);
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
tools=[
{
"type": "web_search",
"user_location": {
"type": "approximate",
"country": "GB",
"city": "London",
"region": "London",
},
}
],
input="What are the best restaurants near me?",
)
print(response.output_text)
package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
tool := responses.ToolParamOfWebSearch(responses.WebSearchToolTypeWebSearch)
tool.OfWebSearch.UserLocation = responses.WebSearchToolUserLocationParam{
Type: "approximate",
Country: openai.String("GB"),
City: openai.String("London"),
Region: openai.String("London"),
}
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Tools: []responses.ToolUnionParam{tool},
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("What are the best restaurants near me?")},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.WebSearchTool;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("What are the best restaurants near me?")
.addTool(
WebSearchTool.builder()
.type(WebSearchTool.Type.WEB_SEARCH)
.userLocation(
WebSearchTool.UserLocation.builder()
.type(WebSearchTool.UserLocation.Type.APPROXIMATE)
.city("London")
.country("GB")
.region("London")
.build())
.build())
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
using OpenAI.Responses;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
ResponsesClient client = new(key);
CreateResponseOptions options = new() { Model = "gpt-6-astra" };
options.Tools.Add(
ResponseTool.CreateWebSearchTool(
userLocation: WebSearchToolLocation.CreateApproximateLocation(
country: "GB",
city: "London",
region: "London"
)
)
);
options.InputItems.Add(
ResponseItem.CreateUserMessageItem("What are the best restaurants near me?")
);
ResponseResult response = await client.CreateResponseAsync(options);
Console.WriteLine(response.GetOutputText());
require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
input: "What are the best restaurants near me?",
tools: [
{
type: :web_search,
user_location: {
type: :approximate,
country: "GB",
city: "London",
region: "London"
}
}
]
)
puts(response.output_text)
curl "https://api.openai.com/v1/responses" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{
"model": "gpt-6-astra",
"tools": [{
"type": "web_search",
"user_location": {
"type": "approximate",
"country": "GB",
"city": "London",
"region": "London"
}
}],
"input": "What are the best restaurants near me?"
}'
실시간 인터넷 접근
웹 검색 도구가 라이브 콘텐츠를 가져올지, 아니면 캐시/색인된 결과만 사용할지 Responses API에서 제어하세요.
web_search도구에external_web_access: false를 설정하면 오프라인/캐시 전용 모드로 실행돼요.- 설정하지 않으면 기본값은
true(실시간 접근)예요. - 프리뷰 변형(
web_search_preview)은 이 파라미터를 무시하고external_web_access가true인 것처럼 동작해요.
실시간 인터넷 접근 제어
curl "https://api.openai.com/v1/responses" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{
"model": "gpt-6-astra",
"tools": [
{ "type": "web_search", "external_web_access": false }
],
"tool_choice": "auto",
"input": "Find when the Eiffel Tower opened to the public and cite the source."
}'
import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-6-astra",
tools: [{ type: "web_search", external_web_access: false }],
tool_choice: "auto",
input: "Find when the Eiffel Tower opened to the public and cite the source.",
});
console.log(response.output_text);
from openai import OpenAI
client = OpenAI()
resp = client.responses.create(
model="gpt-6-astra",
tools=[{"type": "web_search", "external_web_access": False}],
tool_choice="auto",
input="Find when the Eiffel Tower opened to the public and cite the source.",
)
print(resp.output_text)
package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
tool := responses.ToolParamOfWebSearch(responses.WebSearchToolTypeWebSearch)
tool.OfWebSearch.SetExtraFields(map[string]any{"external_web_access": false})
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Tools: []responses.ToolUnionParam{tool},
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Find when the Eiffel Tower opened to the public and cite the source.")},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.JsonValue;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.WebSearchTool;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("Find when the Eiffel Tower opened to the public and cite the source.")
.addTool(
WebSearchTool.builder()
.type(WebSearchTool.Type.WEB_SEARCH)
.putAdditionalProperty("external_web_access", JsonValue.from(false))
.build())
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
input: "Find when the Eiffel Tower opened to the public and cite the source.",
tools: [
{
type: :web_search,
external_web_access: false
}
]
)
puts(response.output_text)
제한 사항
Chat Completions API
Chat Completions API는 웹 검색을 위해 특수화된 검색 모델만 지원해요. 이 모델들은 도메인 필터, 완전한 소스 목록, 실시간 접근 통제, 반환 토큰 예산 통제 같은 Responses API web_search 기능을 지원하지 않아요.
| 모델 | 컨텍스트 창 | 제한 |
|---|---|---|
gpt-5-search-api |
200k | Chat Completions 검색 모델 경로를 사용해요 |
gpt-4o-search-preview |
128k | Chat Completions 검색 모델 경로를 사용해요; 폐기, 2026-07-23 종료 |
gpt-4o-mini-search-preview |
128k | Chat Completions 검색 모델 경로를 사용해요; 폐기, 2026-07-23 종료 |
Responses API
호스티드 web_search 도구를 사용하세요. Responses API는 레거시 통합을 위해 web_search_preview를 여전히 받아들이지만, 새 통합에는 web_search를 사용하세요.
더 큰 모델 컨텍스트 창이 필요하면 gpt-5.5를 사용하세요. 웹 검색 컨텍스트 창은 128k로 유지돼요.
| 모델 | 모델 컨텍스트 창 | 제한 |
|---|---|---|
gpt-4.1 |
1M | 검색 컨텍스트는 128k로 제한 |
gpt-4.1-mini |
1M | 검색 컨텍스트는 128k로 제한 |
o4-mini |
200k | 검색 컨텍스트는 128k로 제한; 폐기, 2026-10-23 종료 |
Responses API 웹 검색의 경우 모델 컨텍스트 창이 더 커도 검색 컨텍스트 창은 128k로 제한돼요.
- 웹 검색은
minimalreasoning의gpt-5를 지원하지 않아요. - reasoning effort를
none으로 설정한gpt-5.4는 더 낮은 품질의 결과를 낼 수 있어요. - Responses API 웹 검색은 기본 모델의 계층별 rate limits을 사용해요.
web_search_preview는filters나return_token_budget을 지원하지 않고external_web_access를 무시해요.tool_choice: "auto"가면 검색은 선택적이에요. 검색이 반드시 실행되어야 한다면tool_choice: "required"또는 특정 웹 검색 도구 선택을 사용하세요.
사용 참고 사항
| API 가용성 | Rate limits | 참고 |
|---|---|---|
|
도구와 함께 사용되는 기본 [모델](https://developers.openai.com/api/docs/models)의 계층별 rate limits과 동일 |
[Pricing](https://developers.openai.com/api/docs/pricing#built-in-tools)
|
더 알아보기 (Learn more)
- Pricing에서 내장 도구 비용을 확인하세요.
- ZDR 및 데이터 상주에서 데이터 통제를 알아보세요.
- function calling과 기타 도구를 참고하세요.