Web search

Web search (웹 검색)

웹 검색은 모델이 인터넷의 최신 정보에 접근하고 출처 인용(sourced citations)이 있는 답변을 제공하게 해줘요. 이를 활성화하려면 Responses API에서 웹 검색 도구를 사용하거나, 경우에 따라 Chat Completions에서 사용해요.

OpenAI 모델에서 사용할 수 있는 웹 검색에는 세 가지 주요 유형이 있어요.

  1. 비추론 웹 검색: 비추론 모델이 사용자 쿼리를 웹 검색 도구로 보내고, 도구가 최상위 결과를 바탕으로 응답을 반환해요. 내부 계획이 없고 모델은 단순히 검색 도구의 응답을 전달해요. 이 방식은 빠르고 빠른 조회에 이상적이에요.
  2. 추론 모델의 에이전틱 검색(agentic search): 모델이 검색 과정을 능동적으로 관리하는 접근 방식이에요. 사고 체인(chain of thought)의 일부로 웹 검색을 수행하고, 결과를 분석하며, 계속 검색할지 결정할 수 있어요. 이 유연성 덕분에 에이전틱 검색은 복잡한 워크플로에 잘 맞지만, 검색이 빠른 조회보다 오래 걸린다는 뜻이기도 해요. 예를 들어 gpt-5.5 같은 모델에서 reasoning 수준을 조정해 검색의 깊이와 지연 시간을 모두 바꿀 수 있어요.
  3. 딥 리서치(deep research): 추론 모델이 깊이 있고 확장된 조사를 수행하는 특수한 에이전트 기반 방식이에요. 모델은 사고 체인의 일부로 웹 검색을 수행하며, 종종 수백 개의 소스에 접근해요. 딥 리서치는 몇 분 동안 실행될 수 있고 background 모드와 함께 쓰는 것이 가장 좋아요. gpt-5.5를 reasoning high 또는 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: 결과의 표준 이미지 URL
  • source_website_url: 이미지가 발견된 페이지
  • thumbnail_url: 가능한 경우 썸네일 URL
  • caption: 가능한 경우 짧은 캡션 또는 설명
{
  "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로 제한돼요.

  • 웹 검색은 minimal reasoning의 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 참고
  [Responses](https://developers.openai.com/api/reference/resources/responses)




  [Chat Completions](https://developers.openai.com/api/reference/resources/chat)




  [Assistants](https://developers.openai.com/api/reference/resources/beta/subresources/assistants)
도구와 함께 사용되는 기본 [모델](https://developers.openai.com/api/docs/models)의 계층별 rate limits과 동일 [Pricing](https://developers.openai.com/api/docs/pricing#built-in-tools)
[ZDR and data residency](https://developers.openai.com/api/docs/guides/your-data)

더 알아보기 (Learn more)