---
title: Salidas estructuradas
url: https://platform.claude.com/docs/es/build-with-claude/structured-outputs
description: Obtén resultados JSON validados de flujos de trabajo de agentes
featureMetadata:
  status: stable
  zdr:
    eligibility: qualified
    note: "Excludes [Covered Models](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention#model-specific-data-retention-requirements). Tus prompts y las salidas de Claude no se almacenan. Solo se almacena en caché el JSON Schema, hasta 24 horas desde su último uso. Esto también abarca el uso estricto de herramientas (`strict: true` en las herramientas), que usa la misma canalización de gramática."
  supportedModels:
    - claude-fable-5-2
    - claude-mythos-5-2
    - claude-fable-5-1
    - claude-mythos-5-1
    - claude-fable-5
    - claude-mythos-5
    - claude-mythos-preview
    - claude-opus-5-5
    - claude-opus-5
    - claude-sonnet-5-5
    - claude-sonnet-5
    - claude-haiku-5-5
    - claude-haiku-4-5-20251001
    - claude-opus-4-8
    - claude-opus-4-7
    - claude-opus-4-6
    - claude-sonnet-4-6
    - claude-opus-4-5-20251101
    - claude-sonnet-4-5-20250929
  supportedPlatforms:
    Claude API: stable
    Amazon Bedrock: stable
    Amazon Bedrock (bedrock-runtime): stable
    Claude Platform on AWS: stable
    Claude Models on Google Cloud (Anthropic managed): stable
    Claude Models on Google Cloud (Google managed): stable
    Microsoft Foundry: stable
---

Las “structured outputs” (salidas estructuradas) restringen las respuestas de Claude para que sigan un esquema específico, lo que garantiza una salida válida y analizable para el procesamiento posterior. Las salidas estructuradas ofrecen dos funciones complementarias:

* **Salidas JSON** (`output_config.format`): Obtén la respuesta de Claude en un formato JSON específico, por ejemplo, para extraer datos de imágenes o texto, generar informes estructurados o dar formato a respuestas de API. Esta página trata sobre las salidas JSON.
* **Uso estricto de herramientas** (`strict: true`): Garantiza la validación del esquema en los nombres y las entradas de las herramientas. Consulta [Uso estricto de herramientas](https://platform.claude.com/docs/es/agents-and-tools/tool-use/strict-tool-use).

Puedes usar estas funciones de forma independiente o [juntas en la misma solicitud](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#using-both-features-together).

## Por qué usar salidas estructuradas

Sin salidas estructuradas, Claude puede generar respuestas JSON mal formadas o entradas de herramientas no válidas que rompen tus aplicaciones. Incluso con prompts cuidadosos, podrías encontrarte con:

* Errores de análisis por sintaxis JSON no válida
* Campos obligatorios faltantes
* Tipos de datos inconsistentes
* Infracciones del esquema que requieren manejo de errores y reintentos

Las salidas estructuradas garantizan respuestas que cumplen con el esquema mediante “constrained decoding” (decodificación restringida):

* **Siempre válidas:** Sin errores de análisis de JSON
* **Con seguridad de tipos:** Tipos de campo y campos obligatorios garantizados
* **Confiables:** No se necesitan reintentos por infracciones del esquema

## Cómo funciona

<Steps>
  <Step title="Define tu esquema">
    Describe la estructura que quieres como un esquema JSON o como un tipo en tu lenguaje. El esquema sigue JSON Schema, con algunas [limitaciones](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#json-schema-limitations).
  </Step>

  <Step title="Envíalo en output_config.format">
    La solicitud incluye el esquema en `output_config.format` con `type: "json_schema"`. Los helpers del SDK lo configuran por ti.
  </Step>

  <Step title="Lee la respuesta">
    Claude devuelve JSON válido que coincide con tu esquema en el bloque de contenido de texto de la respuesta. Los helpers del SDK lo analizan y lo convierten en tu tipo.
  </Step>
</Steps>

## Uso

<Tabs>
  <Tab title="cURL">
    ```bash
    curl https://api.anthropic.com/v1/messages \
      -H "content-type: application/json" \
      -H "x-api-key: $ANTHROPIC_API_KEY" \
      -H "anthropic-version: 2026-10-21" \
      -d '{
        "model": "claude-opus-5-5",
        "max_tokens": 1024,
        "messages": [
          {
            "role": "user",
            "content": "Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm."
          }
        ],
        "output_config": {
          "format": {
            "type": "json_schema",
            "schema": {
              "type": "object",
              "properties": {
                "name": {"type": "string"},
                "email": {"type": "string"},
                "plan_interest": {"type": "string"},
                "demo_requested": {"type": "boolean"}
              },
              "required": ["name", "email", "plan_interest", "demo_requested"],
              "additionalProperties": false
            }
          }
        }
      }'
    ```

    Claude devuelve JSON como este en el bloque de contenido de texto de la respuesta:

    ```json Output
    {
      "name": "John Smith",
      "email": "john@example.com",
      "plan_interest": "Enterprise",
      "demo_requested": true
    }
    ```
  </Tab>

  <Tab title="CLI">
    ```bash
    ant messages create <<'YAML'
    model: claude-opus-5-5
    max_tokens: 1024
    messages:
      - role: user
        content: >-
          Extract the key information from this email: John Smith
          (john@example.com) is interested in our Enterprise plan and wants
          to schedule a demo for next Tuesday at 2pm.
    output_config:
      format:
        type: json_schema
        schema:
          type: object
          properties:
            name: {type: string}
            email: {type: string}
            plan_interest: {type: string}
            demo_requested: {type: boolean}
          required: [name, email, plan_interest, demo_requested]
          additionalProperties: false
    YAML
    ```

    Claude devuelve JSON como este en el bloque de contenido de texto de la respuesta:

    ```json Output
    {
      "name": "John Smith",
      "email": "john@example.com",
      "plan_interest": "Enterprise",
      "demo_requested": true
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python
    from pydantic import BaseModel
    # ...


    class ContactInfo(BaseModel):
        name: str
        email: str
        plan_interest: str
        demo_requested: bool


    client = Anthropic()

    response = client.messages.parse(
        model="claude-opus-5-5",
        max_tokens=1024,
        messages=[
            {
                "role": "user",
                "content": (
                    "Extract the key information from this email: "
                    "John Smith (john@example.com) is interested in our Enterprise plan "
                    "and wants to schedule a demo for next Tuesday at 2pm."
                ),
            }
        ],
        output_format=ContactInfo,
    )

    print(response.parsed_output)
    ```

    Pasa un modelo de [Pydantic](https://docs.pydantic.dev/) a `client.messages.parse()`, el método recomendado, como `output_format`. El SDK transforma el esquema del modelo, lo envía como `output_config.format`, valida la respuesta y devuelve el modelo analizado en `parsed_output`.

    El ejemplo anterior produce:

    ```text Output wrap
    name='John Smith' email='john@example.com' plan_interest='Enterprise' demo_requested=True
    ```

    Si conoces las claves de un campo, usa un modelo anidado de Pydantic, no un `dict`. El SDK envía un campo `dict` como un objeto con `properties` vacío, por lo que Claude solo puede devolver `{}` para él. Si no conoces las claves de antemano, consulta [Objetos con claves arbitrarias](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#objects-with-arbitrary-keys).
  </Tab>

  <Tab title="TypeScript">
    ```typescript
    import { z } from "zod";
    import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";

    const ContactInfoSchema = z.object({
      name: z.string(),
      email: z.string(),
      plan_interest: z.string(),
      demo_requested: z.boolean()
    });

    const client = new Anthropic();

    const response = await client.messages.parse({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      messages: [
        {
          role: "user",
          content:
            "Extract the key information from this email: " +
            "John Smith (john@example.com) is interested in our Enterprise plan " +
            "and wants to schedule a demo for next Tuesday at 2pm."
        }
      ],
      output_config: { format: zodOutputFormat(ContactInfoSchema) }
    });

    // Analizado y validado automáticamente
    console.log(response.parsed_output);
    ```

    Envuelve un esquema de [Zod](https://zod.dev/) en `zodOutputFormat()` y pásalo a `client.messages.parse()` como `output_config.format`. `zodOutputFormat()` transforma el esquema, y `parse()` valida la respuesta y devuelve el resultado analizado en `parsed_output`, tipado para coincidir con el esquema.

    El ejemplo anterior produce:

    ```text Output wrap
    {
      name: 'John Smith',
      email: 'john@example.com',
      plan_interest: 'Enterprise',
      demo_requested: true
    }
    ```

    Si conoces las claves de un campo, usa `z.object()`, no `z.record()`. El SDK envía un campo `z.record()` como un objeto con `properties` vacío, por lo que Claude solo puede devolver `{}` para él. Si no conoces las claves de antemano, consulta [Objetos con claves arbitrarias](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#objects-with-arbitrary-keys).
  </Tab>

  <Tab title="C#">
    ```csharp
    using Anthropic;
    using Anthropic.Models.Messages;
    using Anthropic.Services;

    var client = new AnthropicClient();

    var response = await client.Messages.Create<ContactInfo>(new MessageCreateParams
    {
        Model = Model.ClaudeOpus5_5,
        MaxTokens = 1024,
        Messages = [
            new UserMessageParam
            {
                Content = "Extract the key information from this email: "
                    + "John Smith (john@example.com) is interested in our Enterprise plan "
                    + "and wants to schedule a demo for next Tuesday at 2pm."
            }
        ],
    });

    if (response.Content.Select(block => block.Parsed()).OfType<ContactInfo>().FirstOrDefault() is { } contact)
    {
        Console.WriteLine(contact);
    }

    public record ContactInfo
    {
        public string Name { get; set; } = "";
        public string Email { get; set; } = "";
        public string PlanInterest { get; set; } = "";
        public bool DemoRequested { get; set; }
    }
    ```

    Pasa una clase o un record simple de C# a la sobrecarga genérica `Create<T>()`. El SDK deriva un esquema JSON del tipo, lo transforma y analiza cada bloque de texto de vuelta en él.

    El ejemplo anterior produce:

    ```text Output wrap
    ContactInfo { Name = John Smith, Email = john@example.com, PlanInterest = Enterprise, DemoRequested = True }
    ```
  </Tab>

  <Tab title="Go">
    ```go
    type ContactInfo struct {
    	Name          string `json:"name"`
    	Email         string `json:"email"`
    	PlanInterest  string `json:"plan_interest"`
    	DemoRequested bool   `json:"demo_requested"`
    }

    func main() {
    	client := anthropic.NewClient()

    	var contact ContactInfo
    	_, err := client.Beta.Messages.New(context.Background(), anthropic.BetaMessageNewParams{
    		Model:     anthropic.ModelClaudeOpus5_5,
    		MaxTokens: 1024,
    		Messages: []anthropic.BetaMessageParam{
    			anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock(
    				"Extract the key information from this email: " +
    					"John Smith (john@example.com) is interested in our Enterprise plan " +
    					"and wants to schedule a demo for next Tuesday at 2pm.",
    			)),
    		},
    		OutputConfig: anthropic.BetaOutputConfigParam{
    			Format: anthropic.BetaJSONOutputFormatParam{Schema: &contact},
    		},
    	})
    	if err != nil {
    		panic(err)
    	}

    	fmt.Printf("%#v\n", contact)
    }
    ```

    <Note>
      Pasar un struct como esquema está en beta en Go, por lo que este ejemplo usa `client.Beta.Messages.New`. Para usar el `client.Messages.New` estable, pasa un esquema JSON como se muestra en [Uso de un esquema JSON sin procesar](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#using-a-raw-json-schema).
    </Note>

    El SDK genera y transforma el esquema JSON a partir del struct y deserializa la respuesta en él.

    El ejemplo anterior produce:

    ```text Output wrap
    main.ContactInfo{Name:"John Smith", Email:"john@example.com", PlanInterest:"Enterprise", DemoRequested:true}
    ```
  </Tab>

  <Tab title="Java">
    ```java
    static class ContactInfo {
        public String name;
        public String email;
        public String plan_interest;
        public boolean demo_requested;
    }

    void main() throws Exception {
        AnthropicClient client = AnthropicOkHttpClient.fromEnv();

        StructuredMessageCreateParams<ContactInfo> params = MessageCreateParams.builder()
            .model(Model.CLAUDE_OPUS_5_5)
            .maxTokens(1024)
            .addUserMessage("Extract the key information from this email: "
                + "John Smith (john@example.com) is interested in our Enterprise plan "
                + "and wants to schedule a demo for next Tuesday at 2pm.")
            .outputConfig(ContactInfo.class)
            .build();

        StructuredMessage<ContactInfo> response = client.messages().create(params);
        ContactInfo contact = response.content().stream()
            .flatMap(block -> block.text().stream())
            .findFirst().orElseThrow().text();
        IO.println(new ObjectMapper().writeValueAsString(contact));
    }
    ```

    Cuando pasas una clase Java a `outputConfig()`, el SDK deriva un esquema JSON de ella y valida el esquema. Luego, el builder produce un `StructuredMessageCreateParams<T>`. En lugar de transformar el esquema, la validación local del SDK rechaza las [restricciones no compatibles con la API](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#json-schema-limitations). Lee el resultado analizado con `response.content().stream().flatMap(block -> block.text().stream()).findFirst().orElseThrow().text()`.

    El ejemplo anterior produce:

    ```json Output
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise","demo_requested":true}
    ```

    <Note>
      Declara tus clases de esquema como clases de nivel superior o como clases anidadas `static`. Este requisito proviene de la biblioteca Jackson Databind (`com.fasterxml.jackson.databind`), que el SDK usa para deserializar las respuestas JSON en instancias de tus clases y que no puede instanciar clases internas no estáticas.
    </Note>

    <Accordion title="Borrado de tipos genéricos">
      Java conserva la información de tipos genéricos para los campos en los metadatos de la clase, pero el “generic type erasure” (borrado de tipos genéricos) se aplica en otros ámbitos. Aunque se puede derivar un esquema JSON de un campo `BookList.books` con tipo `List<Book>`, no se puede derivar un esquema JSON válido de una variable local de ese mismo tipo.

      Si ocurre un error al convertir una respuesta JSON en una instancia de una clase Java, el mensaje de error incluye la respuesta JSON para ayudar con el diagnóstico. Si tu respuesta JSON podría contener información confidencial, evita registrarla directamente o asegúrate de ocultar cualquier detalle confidencial del mensaje de error.
    </Accordion>

    <Accordion title="Validación local del esquema">
      Las salidas estructuradas admiten un [subconjunto del lenguaje JSON Schema](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#json-schema-limitations). El SDK genera esquemas automáticamente a partir de clases para ajustarse a este subconjunto. El método `outputConfig(Class<T>)` realiza una comprobación de validación sobre el esquema derivado de la clase especificada.

      Puntos clave:

      * **Validación local:** El SDK valida el esquema sin enviar una solicitud.
      * **Validación remota:** La API también valida el esquema JSON cuando recibe la solicitud.
      * **Compatibilidad de versiones:** Si la versión del SDK está desactualizada, la validación local podría rechazar un esquema que la validación remota acepta.
      * **Desactivar la validación local:** Pasa `JsonSchemaLocalValidation.NO` si encuentras problemas de compatibilidad:

      ```java
      import com.anthropic.core.JsonSchemaLocalValidation;
      // ...

      static class BookList {
          public List<String> books;
      }

      void main() {
          StructuredMessageCreateParams<BookList> createParams = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(2048)
              .outputConfig(BookList.class, JsonSchemaLocalValidation.NO)
              .addUserMessage("List some famous late twentieth century novels.")
              .build();
      }
      ```
    </Accordion>

    <Accordion title="Streaming">
      Las salidas estructuradas también funcionan con streaming. Como las respuestas llegan en eventos de stream, necesitas acumular la respuesta completa antes de deserializar el JSON.

      Usa `MessageAccumulator` para recopilar las cadenas JSON del stream. Una vez acumuladas, llama a `MessageAccumulator.message(Class<T>)` para convertir el `Message` acumulado en un `StructuredMessage`, que deserializa automáticamente el JSON en tu clase Java.
    </Accordion>

    <Accordion title="Propiedades del esquema JSON">
      Cuando el SDK deriva un esquema JSON de tus clases Java, de forma predeterminada incluye todas las propiedades representadas por campos `public` o métodos getter `public` y excluye los campos y métodos getter que no son `public`.

      Puedes controlar la visibilidad con anotaciones:

      * `@JsonIgnore` excluye un campo o método getter `public`.
      * `@JsonProperty` incluye un campo o método getter que no es `public`.

      Si defines campos `private` con métodos getter `public`, el SDK deriva el nombre de la propiedad del getter (por ejemplo, el campo `private` `myValue` con el método `public` `getMyValue()` produce una propiedad `"myValue"`). Para usar un nombre de getter no convencional, anota el método con `@JsonProperty`.

      Cada clase debe definir al menos una propiedad para el esquema JSON. Se produce un error de validación si ningún campo o método getter puede producir propiedades del esquema, por ejemplo, cuando:

      * No hay campos ni métodos getter en la clase
      * Todos los miembros `public` están anotados con `@JsonIgnore`
      * Ningún miembro que no es `public` tiene anotaciones `@JsonProperty`
      * Un campo usa un tipo `Map`, lo que produce un campo `"properties"` vacío (para evitarlo, consulta [Objetos con claves arbitrarias](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#objects-with-arbitrary-keys))
    </Accordion>

    <Accordion title="Composición y herencia">
      Tus clases Java pueden usar composición y herencia para compartir estructura al definir esquemas JSON. Cada patrón afecta la estructura de salida de forma diferente.

      La **composición** produce una salida JSON anidada. Derivar un esquema de la clase `Composed` que compone `A` y `B`:

      ```java
      static class A {
          public String a;
      }

      static class B {
          public String b;
      }

      static class Composed {
          public A composedA;
          public B composedB;
      }
      ```

      La salida JSON tiene esta estructura anidada:

      ```json Output
      {
        "composedA": { "a": "hello" },
        "composedB": { "b": "world" }
      }
      ```

      La **herencia** produce una salida JSON plana. Derivar un esquema de la clase `Derived` que extiende `Base`:

      ```java
      static class Base {
          public String a;
      }

      static class Derived extends Base {
          public String b;
      }
      ```

      La salida JSON tiene esta estructura plana:

      ```json Output
      {
        "a": "hello",
        "b": "world"
      }
      ```
    </Accordion>

    <Accordion title="Anotaciones (Jackson y Swagger)">
      Puedes usar anotaciones de Jackson Databind para enriquecer el esquema JSON derivado de tus clases Java:

      ```java
      import com.fasterxml.jackson.annotation.JsonClassDescription;
      import com.fasterxml.jackson.annotation.JsonIgnore;
      import com.fasterxml.jackson.annotation.JsonPropertyDescription;

      static class Person {

          @JsonPropertyDescription("The first name and surname of the person")
          public String name;

          public int birthYear;

          @JsonPropertyDescription("The year the person died, or 'present' if the person is living.")
          public String deathYear;
      }

      @JsonClassDescription("The details of one published book")
      static class Book {

          public String title;
          public Person author;

          @JsonPropertyDescription("The year in which the book was first published.")
          public int publicationYear;

          @JsonIgnore
          public String genre;
      }

      static class BookList {
          public List<Book> books;
      }
      ```

      Resumen de anotaciones:

      * `@JsonClassDescription`: Agrega una descripción a una clase
      * `@JsonPropertyDescription`: Agrega una descripción a un campo o método getter
      * `@JsonIgnore`: Excluye un campo o getter `public` del esquema
      * `@JsonProperty`: Incluye un campo o getter que no es `public` en el esquema

      Si usas `@JsonProperty(required = false)`, el SDK ignora el valor `false`. Los esquemas derivados de clases siempre marcan todas las propiedades como obligatorias.

      También puedes usar las anotaciones `@Schema` y `@ArraySchema` de Swagger Core (OpenAPI 3) para restricciones específicas de tipo:

      ```java
      import io.swagger.v3.oas.annotations.media.ArraySchema;
      import io.swagger.v3.oas.annotations.media.Schema;

      static class Article {

          @ArraySchema(minItems = 1)
          public List<String> authors;

          public String title;

          @Schema(format = "date")
          public String publicationDate;

          public int pageCount;
      }
      ```

      La validación local comprueba que no hayas usado palabras clave de restricción no compatibles, pero los valores de las restricciones no se validan localmente. Por ejemplo, un valor de `"format"` no compatible podría pasar la validación local pero causar un error remoto.

      Si usas anotaciones de Jackson y de Swagger para configurar el mismo campo del esquema, la anotación de Jackson tiene prioridad.
    </Accordion>
  </Tab>

  <Tab title="PHP">
    ```php
    use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
    use Anthropic\Lib\Contracts\StructuredOutputModel;

    $client = new Client();

    class ContactInfo implements StructuredOutputModel
    {
        use StructuredOutputModelTrait;

        public string $name;
        public string $email;
        public string $plan_interest;
        public bool $demo_requested;
    }

    $message = $client->messages->create(
        maxTokens: 1024,
        messages: [
            [
                'role' => 'user',
                'content' => 'Extract the key information from this email: '
                    . 'John Smith (john@example.com) is interested in our Enterprise plan '
                    . 'and wants to schedule a demo for next Tuesday at 2pm.',
            ],
        ],
        model: 'claude-opus-5-5',
        outputConfig: ['format' => ContactInfo::class],
    );

    $contact = $message->parsedOutput();
    if ($contact instanceof ContactInfo) {
        var_dump($contact);
    }
    ```

    Define una clase que implemente `StructuredOutputModel` y use `StructuredOutputModelTrait`, y pasa su nombre en `outputConfig: ['format' => MyClass::class]`. El SDK deriva un esquema JSON de los tipos de propiedad de PHP 8 de la clase, lo transforma y devuelve una instancia tipada desde `$message->parsedOutput()`.

    `parsedOutput()` devuelve la instancia de tu modelo si tiene éxito, o `null` (o un array de error) si el análisis falla. Usa `instanceof` para acotar el tipo antes de acceder a los campos.

    El ejemplo anterior produce:

    ```text Output wrap
    object(ContactInfo)#42 (4) {
      ["name"]=>
      string(10) "John Smith"
      ["email"]=>
      string(16) "john@example.com"
      ["plan_interest"]=>
      string(10) "Enterprise"
      ["demo_requested"]=>
      bool(true)
    }
    ```

    <Accordion title="Inferencia de tipos">
      El SDK asigna los tipos de propiedad nativos de PHP 8 a JSON Schema:

      | Tipo de PHP                                  | JSON Schema                            |
      | -------------------------------------------- | -------------------------------------- |
      | `string`                                     | `"string"`                             |
      | `int`                                        | `"integer"`                            |
      | `float`                                      | `"number"`                             |
      | `bool`                                       | `"boolean"`                            |
      | `array`                                      | `"array"` (consulta la siguiente nota) |
      | `?type` (anulable)                           | Campo opcional                         |
      | Clase que implementa `StructuredOutputModel` | Objeto anidado                         |

      Para las propiedades `array`, el SDK agrega un esquema `items` solo cuando el tipo de elemento es un `StructuredOutputModel` anidado, declarado con `#[Constrained(itemClass: MyModel::class)]` o con un docblock `/** @var MyModel[] */`. Los arrays de escalares (`string[]`, `int[]`) emiten un `{"type":"array"}` sin restricciones.

      Todas las propiedades no nullable se convierten en campos obligatorios.
    </Accordion>

    <Accordion title="Restricciones con el atributo #[Constrained]">
      Agrega restricciones con el atributo `#[Constrained]`:

      ```php
      use Anthropic\Lib\Attributes\Constrained;
      use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
      use Anthropic\Lib\Contracts\StructuredOutputModel;

      class Address implements StructuredOutputModel { use StructuredOutputModelTrait; public string $street; }

      class Profile implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          #[Constrained(description: 'Age in years', minimum: 0, maximum: 150)]
          public int $age;

          #[Constrained(format: 'email')]
          public string $email;

          #[Constrained(itemClass: Address::class, minItems: 1)]
          public array $addresses;
      }
      ```

      **Restricciones aplicadas por la API** (enviadas en el esquema): `description`, `format`, `const`, `itemClass`, `minItems` (solo 0 o 1).

      **Restricciones validadas por el SDK** (eliminadas del esquema enviado, agregadas a la descripción y validadas contra la respuesta): `minimum`, `maximum`, `multipleOf`, `minLength`, `maxLength`.
    </Accordion>
  </Tab>

  <Tab title="Ruby">
    ```ruby
    client = Anthropic::Client.new

    class ContactInfo < Anthropic::BaseModel
      required :name, String
      required :email, String
      required :plan_interest, String
      required :demo_requested, Anthropic::Boolean
    end

    message = client.messages.create(
      model: Anthropic::Model::CLAUDE_OPUS_5_5,
      max_tokens: 1024,
      messages: [{
        role: "user",
        content: "Extract the key information from this email: " \
          "John Smith (john@example.com) is interested in our Enterprise plan " \
          "and wants to schedule a demo for next Tuesday at 2pm."
      }],
      output_config: { format: ContactInfo }
    )

    contact = message.parsed_output
    puts contact
    ```

    Define una clase que extienda `Anthropic::BaseModel` y pásala a `messages.create()` como `output_config: { format: Model }`. El SDK deriva un esquema JSON de la clase, lo transforma y asigna el resultado analizado al atributo `parsed_output` de la respuesta como un objeto Ruby tipado.

    El ejemplo anterior produce:

    ```text Output wrap
    {name: "John Smith", email: "john@example.com", plan_interest: "Enterprise", demo_requested: true}
    ```

    <Accordion title="Funciones avanzadas de modelos">
      El SDK de Ruby admite funciones adicionales de definición de modelos para esquemas más completos:

      * **Palabra clave `doc:`:** Agrega descripciones a los campos para obtener una salida de esquema más informativa
      * **`Anthropic::ArrayOf[T]`:** Arrays tipados. Pasa las restricciones a nivel de array (`min_items:`, `max_items:`) como palabras clave en `required`/`optional`, no en `ArrayOf` en sí
      * **`Anthropic::EnumOf[:a, :b]`:** Campos enum con valores restringidos
      * **`Anthropic::UnionOf[T1, T2]`:** Tipos unión asignados a `anyOf`

      ```ruby
      class FamousNumber < Anthropic::BaseModel
        required :value, Float
        optional :reason, String, doc: "why is this number mathematically significant?"
      end

      class Output < Anthropic::BaseModel
        required :numbers, Anthropic::ArrayOf[FamousNumber], min_items: 3, max_items: 5
      end

      message = client.messages.create(
        model: Anthropic::Model::CLAUDE_OPUS_5_5,
        max_tokens: 1024,
        messages: [{ role: "user", content: "give me some famous numbers" }],
        output_config: { format: Output }
      )

      message.parsed_output
      # => #<Output numbers=[#<FamousNumber value=3.14159... reason="Pi es...">...]>
      ```
    </Accordion>
  </Tab>
</Tabs>

## Cómo funciona la transformación del SDK

La mayoría de los helpers del SDK transforman los esquemas que usan funciones no compatibles. Los pasos de la transformación son:

1. **Eliminar las restricciones no compatibles** (por ejemplo, `minimum`, `maximum`, `minLength`, `maxLength`)
2. **Actualizar las descripciones** agregando cada restricción no compatible a la descripción del campo (por ejemplo, `{minimum: 100}`)
3. **Agregar `additionalProperties: false`** a todos los objetos
4. **Filtrar los formatos de cadena** para dejar solo la lista compatible
5. **Validar las respuestas** contra tu esquema original y todas sus restricciones, si el helper valida las respuestas

Claude recibe un esquema simplificado, pero un helper que valida las respuestas sigue aplicando cada restricción en tu código.

**Ejemplo:** Un campo con `minimum: 100` se convierte en un entero simple en el esquema enviado, y el SDK agrega `{minimum: 100}` a la descripción del campo. Un helper que valida las respuestas sigue comprobando la respuesta contra `minimum: 100`.

## Uso de un esquema JSON sin procesar

Para usar un esquema JSON de un archivo, de una especificación OpenAPI o de código que lo construye en tiempo de ejecución, pásalo en `output_config.format`.

<Tabs>
  <Tab title="cURL">
    ```bash
    curl https://api.anthropic.com/v1/messages \
      -H "content-type: application/json" \
      -H "x-api-key: $ANTHROPIC_API_KEY" \
      -H "anthropic-version: 2026-10-21" \
      -d '{
        "model": "claude-opus-5-5",
        "max_tokens": 1024,
        "messages": [
          {
            "role": "user",
            "content": "Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm."
          }
        ],
        "output_config": {
          "format": {
            "type": "json_schema",
            "schema": {
              "type": "object",
              "properties": {
                "name": {"type": "string"},
                "email": {"type": "string"},
                "plan_interest": {"type": "string"},
                "demo_requested": {"type": "boolean"}
              },
              "required": ["name", "email", "plan_interest", "demo_requested"],
              "additionalProperties": false
            }
          }
        }
      }'
    ```

    Claude devuelve JSON como este en el bloque de contenido de texto de la respuesta:

    ```json Output
    {
      "name": "John Smith",
      "email": "john@example.com",
      "plan_interest": "Enterprise",
      "demo_requested": true
    }
    ```
  </Tab>

  <Tab title="CLI">
    ```bash
    ant messages create <<'YAML'
    model: claude-opus-5-5
    max_tokens: 1024
    messages:
      - role: user
        content: >-
          Extract the key information from this email: John Smith
          (john@example.com) is interested in our Enterprise plan and wants
          to schedule a demo for next Tuesday at 2pm.
    output_config:
      format:
        type: json_schema
        schema:
          type: object
          properties:
            name: {type: string}
            email: {type: string}
            plan_interest: {type: string}
            demo_requested: {type: boolean}
          required: [name, email, plan_interest, demo_requested]
          additionalProperties: false
    YAML
    ```

    Claude devuelve JSON como este en el bloque de contenido de texto de la respuesta:

    ```json Output
    {
      "name": "John Smith",
      "email": "john@example.com",
      "plan_interest": "Enterprise",
      "demo_requested": true
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python
    client = anthropic.Anthropic()

    response = client.messages.create(
        model="claude-opus-5-5",
        max_tokens=1024,
        messages=[
            {
                "role": "user",
                "content": (
                    "Extract the key information from this email: "
                    "John Smith (john@example.com) is interested in our Enterprise plan "
                    "and wants to schedule a demo for next Tuesday at 2pm."
                ),
            }
        ],
        output_config={
            "format": {
                "type": "json_schema",
                "schema": {
                    "type": "object",
                    "properties": {
                        "name": {"type": "string"},
                        "email": {"type": "string"},
                        "plan_interest": {"type": "string"},
                        "demo_requested": {"type": "boolean"},
                    },
                    "required": ["name", "email", "plan_interest", "demo_requested"],
                    "additionalProperties": False,
                },
            }
        },
    )
    text = next(block.text for block in response.content if block.type == "text")
    contact = json.loads(text)
    print(contact)
    ```

    El ejemplo anterior produce:

    ```text Output wrap
    {'name': 'John Smith', 'email': 'john@example.com', 'plan_interest': 'Enterprise', 'demo_requested': True}
    ```

    Para mover las [restricciones no compatibles con la API](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#json-schema-limitations) a las descripciones de los campos, pasa el esquema por `transform_schema()` del paquete `anthropic` antes de enviarlo, y edita el resultado si lo necesitas. `transform_schema()` también acepta un modelo de Pydantic. A diferencia de `client.messages.parse()`, devuelve el esquema transformado en lugar de enviarlo.
  </Tab>

  <Tab title="TypeScript">
    ```typescript
    import { jsonSchemaOutputFormat } from "@anthropic-ai/sdk/helpers/json-schema";

    const client = new Anthropic();

    const response = await client.messages.parse({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      messages: [
        {
          role: "user",
          content: "Extract contact info: John Smith, john@example.com, interested in the Pro plan"
        }
      ],
      output_config: {
        format: jsonSchemaOutputFormat({
          type: "object",
          properties: {
            name: { type: "string" },
            email: { type: "string" },
            planInterest: { type: "string" }
          },
          required: ["name", "email", "planInterest"],
          additionalProperties: false
        } as const)
      }
    });

    // response.parsed_output tiene el tipo { name: string; email: string; planInterest: string } | null
    console.log(response.parsed_output);
    ```

    Usa `jsonSchemaOutputFormat()` para pasar un esquema JSON simple a `parse()`, sin instalar Zod. La API devuelve una respuesta que cumple con el esquema y, si declaras el esquema con `as const`, TypeScript infiere automáticamente el tipo de `parsed_output` según tu esquema. Para un esquema importado o generado, `parsed_output` se tipa como `unknown`.

    De forma predeterminada, el helper [transforma el esquema](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#how-sdk-transformation-works) de la misma manera que `zodOutputFormat()`. Pasa `{ transform: false }` como segundo argumento para enviarlo sin cambios. A diferencia de `zodOutputFormat()`, no valida la respuesta, por lo que no detecta infracciones de las [restricciones no compatibles con la API](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#json-schema-limitations), como `minimum`.

    El ejemplo anterior produce:

    ```text Output wrap
    { name: 'John Smith', email: 'john@example.com', planInterest: 'Pro' }
    ```
  </Tab>

  <Tab title="C#">
    ```csharp
    using System.Text.Json;
    using Anthropic;
    using Anthropic.Models.Messages;

    var client = new AnthropicClient();

    var response = await client.Messages.Create(new MessageCreateParams
    {
        Model = Model.ClaudeOpus5_5,
        MaxTokens = 1024,
        Messages = [
            new UserMessageParam
            {
                Content = "Extract the key information from this email: "
                    + "John Smith (john@example.com) is interested in our Enterprise plan "
                    + "and wants to schedule a demo for next Tuesday at 2pm."
            }
        ],
        OutputConfig = new OutputConfig
        {
            Format = new JsonOutputFormat
            {
                Schema = new Dictionary<string, JsonElement>
                {
                    ["type"] = JsonSerializer.SerializeToElement("object"),
                    ["properties"] = JsonSerializer.SerializeToElement(new
                    {
                        name = new { type = "string" },
                        email = new { type = "string" },
                        plan_interest = new { type = "string" },
                        demo_requested = new { type = "boolean" },
                    }),
                    ["required"] = JsonSerializer.SerializeToElement(
                        new[] { "name", "email", "plan_interest", "demo_requested" }),
                    ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
                },
            },
        },
    });

    if (response.Content.Select(block => block.Value).OfType<TextBlock>().FirstOrDefault() is { } textBlock)
    {
        // Se garantiza que el JSON coincide con el esquema
        var contact = JsonSerializer.Deserialize<JsonElement>(textBlock.Text);
        Console.WriteLine(contact);
    }
    ```

    El ejemplo anterior produce:

    ```json Output
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise","demo_requested":true}
    ```
  </Tab>

  <Tab title="Go">
    ```go
    type ContactInfo struct {
    	Name          string `json:"name"`
    	Email         string `json:"email"`
    	PlanInterest  string `json:"plan_interest"`
    	DemoRequested bool   `json:"demo_requested"`
    }

    func main() {
    	client := anthropic.NewClient()

    	message, _ := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
    		Model:     anthropic.ModelClaudeOpus5_5,
    		MaxTokens: 1024,
    		Messages: []anthropic.MessageParam{
    			anthropic.NewUserMessage(anthropic.NewTextBlock(
    				"Extract the key information from this email: " +
    					"John Smith (john@example.com) is interested in our Enterprise plan " +
    					"and wants to schedule a demo for next Tuesday at 2pm.",
    			)),
    		},
    		OutputConfig: anthropic.OutputConfigParam{
    			Format: anthropic.JSONOutputFormatParam{
    				Schema: map[string]any{
    					"type": "object",
    					"properties": map[string]any{
    						"name":           map[string]string{"type": "string"},
    						"email":          map[string]string{"type": "string"},
    						"plan_interest":  map[string]string{"type": "string"},
    						"demo_requested": map[string]string{"type": "boolean"},
    					},
    					"required":             []string{"name", "email", "plan_interest", "demo_requested"},
    					"additionalProperties": false,
    				},
    			},
    		},
    	})

    	for _, block := range message.Content {
    		switch variant := block.AsAny().(type) {
    		case anthropic.TextBlock:
    			var contact ContactInfo
    			json.Unmarshal([]byte(variant.Text), &contact)
    			fmt.Printf("%#v\n", contact)
    		}
    	}
    }
    ```

    El ejemplo anterior produce:

    ```text Output wrap
    main.ContactInfo{Name:"John Smith", Email:"john@example.com", PlanInterest:"Enterprise", DemoRequested:true}
    ```

    Para que el SDK transforme un esquema de tipo map, pásalo por `BetaJSONSchemaOutputFormat()` en la API beta.
  </Tab>

  <Tab title="Java">
    ```java
    import com.anthropic.core.JsonValue;
    import com.anthropic.models.messages.JsonOutputFormat;
    // ...
    import com.anthropic.models.messages.OutputConfig;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;

    void main() throws Exception {
        AnthropicClient client = AnthropicOkHttpClient.fromEnv();

        JsonOutputFormat.Schema schema = JsonOutputFormat.Schema.builder()
            .putAdditionalProperty("type", JsonValue.from("object"))
            .putAdditionalProperty("properties", JsonValue.from(Map.of(
                "name", Map.of("type", "string"),
                "email", Map.of("type", "string"),
                "plan_interest", Map.of("type", "string"))))
            .putAdditionalProperty("required", JsonValue.from(
                List.of("name", "email", "plan_interest")))
            .putAdditionalProperty("additionalProperties", JsonValue.from(false))
            .build();

        OutputConfig outputConfig = OutputConfig.builder()
            .format(JsonOutputFormat.builder()
                .schema(schema)
                .build())
            .build();

        MessageCreateParams createParams = MessageCreateParams.builder()
            .model(Model.CLAUDE_OPUS_5_5)
            .maxTokens(1024)
            .outputConfig(outputConfig)
            .addUserMessage(
                "John Smith (john@example.com) is interested in our Enterprise plan.")
            .build();

        String json = client.messages().create(createParams).content().stream()
            .flatMap(contentBlock -> contentBlock.text().stream())
            .findFirst()
            .orElseThrow()
            .text();

        JsonNode contact = new ObjectMapper().readTree(json);
        IO.println(contact);
    }
    ```

    El ejemplo anterior produce:

    ```json Output
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise"}
    ```

    Para ver un ejemplo más extenso que construye un esquema anidado con arrays y descripciones, consulta [`StructuredOutputsRawExample.java`](https://github.com/anthropics/anthropic-sdk-java/blob/main/anthropic-java-example/src/main/java/com/anthropic/example/StructuredOutputsRawExample.java) en el repositorio del SDK.
  </Tab>

  <Tab title="PHP">
    ```php
    use Anthropic\Messages\OutputConfig;
    use Anthropic\Messages\JSONOutputFormat;

    $client = new Client();

    $message = $client->messages->create(
        maxTokens: 1024,
        messages: [
            [
                'role' => 'user',
                'content' => 'Extract the key information from this email: '
                    . 'John Smith (john@example.com) is interested in our Enterprise plan.',
            ],
        ],
        model: 'claude-opus-5-5',
        outputConfig: OutputConfig::with(format: JSONOutputFormat::with(schema: [
            'type' => 'object',
            'properties' => [
                'name' => ['type' => 'string'],
                'email' => ['type' => 'string'],
                'plan_interest' => ['type' => 'string'],
            ],
            'required' => ['name', 'email', 'plan_interest'],
            'additionalProperties' => false,
        ])),
    );

    $textBlock = array_find($message->content, static fn ($block): bool => $block instanceof \Anthropic\Messages\TextBlock)
        ?? throw new \RuntimeException('Expected a text block in the response.');
    $contact = json_decode($textBlock->text, associative: true);
    var_dump($contact);
    ```

    Pasa el esquema como un array asociativo a `JSONOutputFormat::with()` dentro de `OutputConfig::with()` y decodifica la respuesta con `json_decode()`.

    El ejemplo anterior produce:

    ```text Output wrap
    array(3) {
      ["name"]=>
      string(10) "John Smith"
      ["email"]=>
      string(16) "john@example.com"
      ["plan_interest"]=>
      string(10) "Enterprise"
    }
    ```
  </Tab>

  <Tab title="Ruby">
    ```ruby
    client = Anthropic::Client.new

    response = client.messages.create(
      model: Anthropic::Model::CLAUDE_OPUS_5_5,
      max_tokens: 1024,
      messages: [
        {
          role: "user",
          content: "Extract the key information from this email: " \
            "John Smith (john@example.com) is interested in our Enterprise plan " \
            "and wants to schedule a demo for next Tuesday at 2pm."
        }
      ],
      output_config: {
        format: {
          type: "json_schema",
          schema: {
            type: "object",
            properties: {
              name: { type: "string" },
              email: { type: "string" },
              plan_interest: { type: "string" },
              demo_requested: { type: "boolean" }
            },
            required: ["name", "email", "plan_interest", "demo_requested"],
            additionalProperties: false
          }
        }
      }
    )

    text = response.content.find { it.type == :text }.text
    contact = JSON.parse(text)
    p contact
    ```

    El ejemplo anterior produce:

    ```text Output wrap
    {"name" => "John Smith", "email" => "john@example.com", "plan_interest" => "Enterprise", "demo_requested" => true}
    ```
  </Tab>
</Tabs>

## Casos de uso comunes

<AccordionGroup>
  <Accordion title="Extracción de datos">
    Extrae datos estructurados de texto no estructurado:

    <CodeGroup>
      ```bash cURL
      curl https://api.anthropic.com/v1/messages \
        -H "content-type: application/json" \
        -H "x-api-key: $ANTHROPIC_API_KEY" \
        -H "anthropic-version: 2026-10-21" \
        -d '{
          "model": "claude-opus-5-5",
          "max_tokens": 4096,
          "messages": [
            {
              "role": "user",
              "content": "Extract invoice data from: Invoice #12345, Date: 2024-01-15, Customer: Acme Corp, 2 widgets at $150.00 each, 1 setup fee at $200.00, Total: $500.00"
            }
          ],
          "output_config": {
            "format": {
              "type": "json_schema",
              "schema": {
                "type": "object",
                "properties": {
                  "invoice_number": {"type": "string"},
                  "date": {"type": "string"},
                  "total_amount": {"type": "number"},
                  "line_items": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "description": {"type": "string"},
                        "quantity": {"type": "number"},
                        "unit_price": {"type": "number"}
                      },
                      "required": ["description", "quantity", "unit_price"],
                      "additionalProperties": false
                    }
                  },
                  "customer_name": {"type": "string"}
                },
                "required": ["invoice_number", "date", "total_amount", "line_items", "customer_name"],
                "additionalProperties": false
              }
            }
          }
        }'
      ```

      ```bash CLI
      ant messages create <<'YAML'
      model: claude-opus-5-5
      max_tokens: 4096
      messages:
        - role: user
          content: |
            Extract invoice data from:
            Invoice #12345
            Date: 2024-01-15
            Customer: Acme Corp
            2 widgets at $150.00 each
            1 setup fee at $200.00
            Total: $500.00
      output_config:
        format:
          type: json_schema
          schema:
            type: object
            properties:
              invoice_number: {type: string}
              date: {type: string}
              total_amount: {type: number}
              line_items:
                type: array
                items:
                  type: object
                  properties:
                    description: {type: string}
                    quantity: {type: number}
                    unit_price: {type: number}
                  required: [description, quantity, unit_price]
                  additionalProperties: false
              customer_name: {type: string}
            required: [invoice_number, date, total_amount, line_items, customer_name]
            additionalProperties: false
      YAML
      ```

      ```python Python
      from pydantic import BaseModel


      class LineItem(BaseModel):
          description: str
          quantity: float
          unit_price: float


      class Invoice(BaseModel):
          invoice_number: str
          date: str
          total_amount: float
          line_items: list[LineItem]
          customer_name: str


      client = anthropic.Anthropic()
      invoice_text = """
      Invoice #12345
      Date: 2024-01-15
      Customer: Acme Corp
      2 widgets at $150.00 each
      1 setup fee at $200.00
      Total: $500.00
      """

      response = client.messages.parse(
          model="claude-opus-5-5",
          max_tokens=4096,
          output_format=Invoice,
          messages=[
              {"role": "user", "content": f"Extract invoice data from: {invoice_text}"}
          ],
      )

      print(response.parsed_output)
      ```

      ```typescript TypeScript
      import { z } from "zod";
      import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";

      const client = new Anthropic();

      const LineItemSchema = z.object({
        description: z.string(),
        quantity: z.number(),
        unit_price: z.number()
      });

      const InvoiceSchema = z.object({
        invoice_number: z.string(),
        date: z.string(),
        total_amount: z.number(),
        line_items: z.array(LineItemSchema),
        customer_name: z.string()
      });

      const invoiceText = `
      Invoice #12345
      Date: 2024-01-15
      Customer: Acme Corp
      2 widgets at $150.00 each
      1 setup fee at $200.00
      Total: $500.00
      `;

      const response = await client.messages.parse({
        model: "claude-opus-5-5",
        max_tokens: 4096,
        output_config: { format: zodOutputFormat(InvoiceSchema) },
        messages: [{ role: "user", content: `Extract invoice data from: ${invoiceText}` }]
      });
      console.log(response.parsed_output);
      ```

      ```csharp C#
      AnthropicClient client = new();

      string invoiceText = """
          Invoice #12345
          Date: 2024-01-15
          Customer: Acme Corp
          2 widgets at $150.00 each
          1 setup fee at $200.00
          Total: $500.00
          """;

      var parameters = new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 4096,
          OutputConfig = new OutputConfig
          {
              Format = new JsonOutputFormat
              {
                  Schema = new Dictionary<string, JsonElement>
                  {
                      ["type"] = JsonSerializer.SerializeToElement("object"),
                      ["properties"] = JsonSerializer.SerializeToElement(new
                      {
                          invoice_number = new { type = "string" },
                          date = new { type = "string" },
                          total_amount = new { type = "number" },
                          line_items = new
                          {
                              type = "array",
                              items = new
                              {
                                  type = "object",
                                  properties = new
                                  {
                                      description = new { type = "string" },
                                      quantity = new { type = "number" },
                                      unit_price = new { type = "number" },
                                  },
                                  required = new[] { "description", "quantity", "unit_price" },
                                  additionalProperties = false,
                              },
                          },
                          customer_name = new { type = "string" },
                      }),
                      ["required"] = JsonSerializer.SerializeToElement(new[] { "invoice_number", "date", "total_amount", "line_items", "customer_name" }),
                      ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
                  },
              },
          },
          Messages = [new UserMessageParam { Content = $"Extract invoice data from: {invoiceText}" }]
      };

      var message = await client.Messages.Create(parameters);
      Console.WriteLine(message);
      ```

      ```go Go
      client := anthropic.NewClient()

      invoiceText := `
      Invoice #12345
      Date: 2024-01-15
      Customer: Acme Corp
      2 widgets at $150.00 each
      1 setup fee at $200.00
      Total: $500.00
      `

      schema := map[string]any{
      	"type":                 "object",
      	"additionalProperties": false,
      	"properties": map[string]any{
      		"invoice_number": map[string]any{"type": "string"},
      		"date":           map[string]any{"type": "string"},
      		"total_amount":   map[string]any{"type": "number"},
      		"line_items": map[string]any{
      			"type": "array",
      			"items": map[string]any{
      				"type":                 "object",
      				"additionalProperties": false,
      				"properties": map[string]any{
      					"description": map[string]any{"type": "string"},
      					"quantity":    map[string]any{"type": "number"},
      					"unit_price":  map[string]any{"type": "number"},
      				},
      				"required": []string{"description", "quantity", "unit_price"},
      			},
      		},
      		"customer_name": map[string]any{"type": "string"},
      	},
      	"required": []string{"invoice_number", "date", "total_amount", "line_items", "customer_name"},
      }

      response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
      	Model:     anthropic.ModelClaudeOpus5_5,
      	MaxTokens: 4096,
      	OutputConfig: anthropic.OutputConfigParam{
      		Format: anthropic.JSONOutputFormatParam{
      			Schema: schema,
      		},
      	},
      	Messages: []anthropic.MessageParam{
      		anthropic.NewUserMessage(anthropic.NewTextBlock(fmt.Sprintf("Extract invoice data from: %s", invoiceText))),
      	},
      })
      if err != nil {
      	log.Fatal(err)
      }
      for _, block := range response.Content {
      	switch variant := block.AsAny().(type) {
      	case anthropic.TextBlock:
      		fmt.Println(variant.Text)
      	}
      }
      ```

      ```java Java
      import com.fasterxml.jackson.annotation.JsonProperty;

      static class LineItem {
          @JsonProperty("description")
          public String description;

          @JsonProperty("quantity")
          public double quantity;

          @JsonProperty("unit_price")
          public double unitPrice;
      }

      static class Invoice {
          @JsonProperty("invoice_number")
          public String invoiceNumber;

          @JsonProperty("date")
          public String date;

          @JsonProperty("total_amount")
          public double totalAmount;

          @JsonProperty("line_items")
          public List<LineItem> lineItems;

          @JsonProperty("customer_name")
          public String customerName;
      }

      void main() {
          AnthropicClient client = AnthropicOkHttpClient.fromEnv();

          String invoiceText = """
              Invoice #12345
              Date: 2024-01-15
              Customer: Acme Corp
              2 widgets at $150.00 each
              1 setup fee at $200.00
              Total: $500.00
              """;

          StructuredMessageCreateParams<Invoice> params = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(4096L)
              .outputConfig(Invoice.class)
              .addUserMessage("Extract invoice data from: " + invoiceText)
              .build();

          StructuredMessage<Invoice> response = client.messages().create(params);
          Invoice invoice = response.content().stream()
              .flatMap(block -> block.text().stream())
              .findFirst().orElseThrow().text();
          IO.println("Invoice " + invoice.invoiceNumber + ": $" + invoice.totalAmount);
          for (LineItem item : invoice.lineItems) {
              IO.println(item.quantity + " x " + item.description);
          }
      }
      ```

      ```php PHP
      use Anthropic\Lib\Attributes\Constrained;
      use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
      use Anthropic\Lib\Contracts\StructuredOutputModel;

      $client = new Client();

      class LineItem implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          public string $description;
          public float $quantity;
          public float $unit_price;
      }

      class Invoice implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          public string $invoice_number;
          public string $date;
          public float $total_amount;
          #[Constrained(itemClass: LineItem::class)]
          public array $line_items;
          public string $customer_name;
      }

      $invoiceText = <<<'TEXT'
      Invoice #12345
      Date: 2024-01-15
      Customer: Acme Corp
      2 widgets at $150.00 each
      1 setup fee at $200.00
      Total: $500.00
      TEXT;

      $message = $client->messages->create(
          maxTokens: 4096,
          messages: [
              ['role' => 'user', 'content' => "Extract invoice data from: $invoiceText"]
          ],
          model: 'claude-opus-5-5',
          outputConfig: ['format' => Invoice::class],
      );

      $invoice = $message->parsedOutput();
      if ($invoice instanceof Invoice) {
          echo "Invoice {$invoice->invoice_number}: \${$invoice->total_amount}\n";
          foreach ($invoice->line_items as $item) {
              echo "{$item->quantity} x {$item->description}\n";
          }
      }
      ```

      ```ruby Ruby
      client = Anthropic::Client.new

      class LineItem < Anthropic::BaseModel
        required :description, String
        required :quantity, Float
        required :unit_price, Float
      end

      class Invoice < Anthropic::BaseModel
        required :invoice_number, String
        required :date, String
        required :total_amount, Float
        required :line_items, Anthropic::ArrayOf[LineItem]
        required :customer_name, String
      end

      invoice_text = <<~TEXT
        Invoice #12345
        Date: 2024-01-15
        Customer: Acme Corp
        2 widgets at $150.00 each
        1 setup fee at $200.00
        Total: $500.00
      TEXT

      message = client.messages.create(
        model: Anthropic::Model::CLAUDE_OPUS_5_5,
        max_tokens: 4096,
        output_config: { format: Invoice },
        messages: [
          { role: "user", content: "Extract invoice data from: #{invoice_text}" }
        ]
      )

      invoice = message.parsed_output
      puts "Invoice #{invoice.invoice_number}: $#{invoice.total_amount}"
      invoice.line_items.each { |item| puts "#{item.quantity} x #{item.description}" }
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Clasificación">
    Clasifica contenido con categorías estructuradas:

    <CodeGroup>
      ```bash cURL
      curl https://api.anthropic.com/v1/messages \
        -H "content-type: application/json" \
        -H "x-api-key: $ANTHROPIC_API_KEY" \
        -H "anthropic-version: 2026-10-21" \
        -d '{
          "model": "claude-opus-5-5",
          "max_tokens": 1024,
          "messages": [
            {
              "role": "user",
              "content": "Classify this feedback: Great product, fast shipping!"
            }
          ],
          "output_config": {
            "format": {
              "type": "json_schema",
              "schema": {
                "type": "object",
                "properties": {
                  "category": {"type": "string"},
                  "confidence": {"type": "number"},
                  "tags": {"type": "array", "items": {"type": "string"}},
                  "sentiment": {"type": "string"}
                },
                "required": ["category", "confidence", "tags", "sentiment"],
                "additionalProperties": false
              }
            }
          }
        }'
      ```

      ```bash CLI
      ant messages create <<'YAML'
      model: claude-opus-5-5
      max_tokens: 1024
      messages:
        - role: user
          content: "Classify this feedback: Great product, fast shipping!"
      output_config:
        format:
          type: json_schema
          schema:
            type: object
            properties:
              category:
                type: string
              confidence:
                type: number
              tags:
                type: array
                items:
                  type: string
              sentiment:
                type: string
            required:
              - category
              - confidence
              - tags
              - sentiment
            additionalProperties: false
      YAML
      ```

      ```python Python
      from pydantic import BaseModel

      client = Anthropic()


      class Classification(BaseModel):
          category: str
          confidence: float
          tags: list[str]
          sentiment: str


      feedback_text = "Great product, but the delivery was slow."
      response = client.messages.parse(
          model="claude-opus-5-5",
          max_tokens=1024,
          output_format=Classification,
          messages=[{"role": "user", "content": f"Classify this feedback: {feedback_text}"}],
      )

      print(response.parsed_output)
      ```

      ```typescript TypeScript
      import { z } from "zod";
      import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";

      const client = new Anthropic();

      const ClassificationSchema = z.object({
        category: z.string(),
        confidence: z.number(),
        tags: z.array(z.string()),
        sentiment: z.string()
      });

      const feedbackText = "Great product, but the delivery was slow.";
      const response = await client.messages.parse({
        model: "claude-opus-5-5",
        max_tokens: 1024,
        output_config: { format: zodOutputFormat(ClassificationSchema) },
        messages: [{ role: "user", content: `Classify this feedback: ${feedbackText}` }]
      });

      console.log(response.parsed_output);
      ```

      ```csharp C#
      AnthropicClient client = new();

      string feedbackText = "Great product, fast shipping!";

      var parameters = new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 1024,
          Messages = [new UserMessageParam { Content = $"Classify this feedback: {feedbackText}" }],
          OutputConfig = new OutputConfig
          {
              Format = new JsonOutputFormat
              {
                  Schema = new Dictionary<string, JsonElement>
                  {
                      ["type"] = JsonSerializer.SerializeToElement("object"),
                      ["properties"] = JsonSerializer.SerializeToElement(new
                      {
                          category = new { type = "string" },
                          confidence = new { type = "number" },
                          tags = new { type = "array", items = new { type = "string" } },
                          sentiment = new { type = "string" },
                      }),
                      ["required"] = JsonSerializer.SerializeToElement(new[] { "category", "confidence", "tags", "sentiment" }),
                      ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
                  },
              },
          },
      };

      var message = await client.Messages.Create(parameters);
      Console.WriteLine(message);
      ```

      ```go Go
      client := anthropic.NewClient()

      feedbackText := "Great product, fast shipping!"

      schema := map[string]any{
      	"type": "object",
      	"properties": map[string]any{
      		"category":   map[string]any{"type": "string"},
      		"confidence": map[string]any{"type": "number"},
      		"tags":       map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
      		"sentiment":  map[string]any{"type": "string"},
      	},
      	"required":             []string{"category", "confidence", "tags", "sentiment"},
      	"additionalProperties": false,
      }

      response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
      	Model:     anthropic.ModelClaudeOpus5_5,
      	MaxTokens: 1024,
      	OutputConfig: anthropic.OutputConfigParam{
      		Format: anthropic.JSONOutputFormatParam{
      			Schema: schema,
      		},
      	},
      	Messages: []anthropic.MessageParam{
      		anthropic.NewUserMessage(anthropic.NewTextBlock(fmt.Sprintf("Classify this feedback: %s", feedbackText))),
      	},
      })
      if err != nil {
      	log.Fatal(err)
      }
      for _, block := range response.Content {
      	switch variant := block.AsAny().(type) {
      	case anthropic.TextBlock:
      		var result map[string]any
      		json.Unmarshal([]byte(variant.Text), &result)
      		fmt.Println(result)
      	}
      }
      ```

      ```java Java
      import com.fasterxml.jackson.annotation.JsonProperty;

      static class Classification {
          @JsonProperty("category")
          public String category;

          @JsonProperty("confidence")
          public double confidence;

          @JsonProperty("tags")
          public List<String> tags;

          @JsonProperty("sentiment")
          public String sentiment;
      }

      void main() {
          AnthropicClient client = AnthropicOkHttpClient.fromEnv();

          String feedbackText = "Great product, fast shipping!";

          StructuredMessageCreateParams<Classification> params = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(1024L)
              .outputConfig(Classification.class)
              .addUserMessage("Classify this feedback: " + feedbackText)
              .build();

          StructuredMessage<Classification> response = client.messages().create(params);
          Classification result = response.content().stream()
              .flatMap(block -> block.text().stream())
              .findFirst().orElseThrow().text();
          IO.println(result.category + " (" + result.confidence + ")");
      }
      ```

      ```php PHP
      use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
      use Anthropic\Lib\Contracts\StructuredOutputModel;

      $client = new Client();

      class Classification implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          public string $category;
          public float $confidence;
          public array $tags;
          public string $sentiment;
      }

      $feedbackText = 'Great product, fast shipping!';

      $message = $client->messages->create(
          maxTokens: 1024,
          messages: [
              ['role' => 'user', 'content' => "Classify this feedback: {$feedbackText}"]
          ],
          model: 'claude-opus-5-5',
          outputConfig: ['format' => Classification::class],
      );

      $result = $message->parsedOutput();
      if ($result instanceof Classification) {
          echo "{$result->category} ({$result->confidence}): {$result->sentiment}\n";
      }
      ```

      ```ruby Ruby
      client = Anthropic::Client.new

      class Classification < Anthropic::BaseModel
        required :category, String
        required :confidence, Float
        required :tags, Anthropic::ArrayOf[String]
        required :sentiment, String
      end

      feedback_text = "Great product, fast shipping!"

      message = client.messages.create(
        model: Anthropic::Model::CLAUDE_OPUS_5_5,
        max_tokens: 1024,
        output_config: { format: Classification },
        messages: [
          { role: "user", content: "Classify this feedback: #{feedback_text}" }
        ]
      )
      puts message.parsed_output
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Formato de respuestas de API">
    Genera respuestas listas para la API:

    <CodeGroup>
      ```bash cURL
      curl https://api.anthropic.com/v1/messages \
        -H "content-type: application/json" \
        -H "x-api-key: $ANTHROPIC_API_KEY" \
        -H "anthropic-version: 2026-10-21" \
        -d '{
          "model": "claude-opus-5-5",
          "max_tokens": 1024,
          "messages": [
            {
              "role": "user",
              "content": "Write an API response: request req_2abc3def has an invalid email."
            }
          ],
          "output_config": {
            "format": {
              "type": "json_schema",
              "schema": {
                "type": "object",
                "properties": {
                  "status": {"type": "string"},
                  "data": {
                    "type": "object",
                    "properties": {"message": {"type": "string"}},
                    "required": ["message"],
                    "additionalProperties": false
                  },
                  "errors": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "code": {"type": "string"},
                        "detail": {"type": "string"}
                      },
                      "required": ["code", "detail"],
                      "additionalProperties": false
                    }
                  },
                  "metadata": {
                    "type": "object",
                    "properties": {"request_id": {"type": "string"}},
                    "required": ["request_id"],
                    "additionalProperties": false
                  }
                },
                "required": ["status", "data", "metadata"],
                "additionalProperties": false
              }
            }
          }
        }'
      ```

      ```bash CLI
      ant messages create <<'YAML'
      model: claude-opus-5-5
      max_tokens: 1024
      output_config:
        format:
          type: json_schema
          schema:
            type: object
            properties:
              status:
                type: string
              data:
                type: object
                properties:
                  message:
                    type: string
                required:
                  - message
                additionalProperties: false
              errors:
                type: array
                items:
                  type: object
                  properties:
                    code:
                      type: string
                    detail:
                      type: string
                  required:
                    - code
                    - detail
                  additionalProperties: false
              metadata:
                type: object
                properties:
                  request_id:
                    type: string
                required:
                  - request_id
                additionalProperties: false
            required:
              - status
              - data
              - metadata
            additionalProperties: false
      messages:
        - role: user
          content: "Write an API response: request req_2abc3def has an invalid email."
      YAML
      ```

      ```python Python
      from pydantic import BaseModel

      client = Anthropic()


      class Payload(BaseModel):
          message: str


      class APIError(BaseModel):
          code: str
          detail: str


      class Metadata(BaseModel):
          request_id: str


      class APIResponse(BaseModel):
          status: str
          data: Payload
          errors: list[APIError] | None = None
          metadata: Metadata


      response = client.messages.parse(
          model="claude-opus-5-5",
          max_tokens=1024,
          output_format=APIResponse,
          messages=[
              {
                  "role": "user",
                  "content": "Write an API response: request req_2abc3def has an invalid email.",
              }
          ],
      )

      print(response.parsed_output)
      ```

      ```typescript TypeScript
      import { z } from "zod";
      import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";

      const client = new Anthropic();

      const APIResponseSchema = z.object({
        status: z.string(),
        data: z.object({ message: z.string() }),
        errors: z.array(z.object({ code: z.string(), detail: z.string() })).optional(),
        metadata: z.object({ request_id: z.string() })
      });

      const response = await client.messages.parse({
        model: "claude-opus-5-5",
        max_tokens: 1024,
        output_config: { format: zodOutputFormat(APIResponseSchema) },
        messages: [
          {
            role: "user",
            content: "Write an API response: request req_2abc3def has an invalid email."
          }
        ]
      });

      console.log(response.parsed_output);
      ```

      ```csharp C#
      AnthropicClient client = new();

      var parameters = new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 1024,
          Messages =
          [
              new UserMessageParam
              {
                  Content = "Write an API response: request req_2abc3def has an invalid email.",
              },
          ],
          OutputConfig = new OutputConfig
          {
              Format = new JsonOutputFormat
              {
                  Schema = new Dictionary<string, JsonElement>
                  {
                      ["type"] = JsonSerializer.SerializeToElement("object"),
                      ["properties"] = JsonSerializer.SerializeToElement(new
                      {
                          status = new { type = "string" },
                          data = new
                          {
                              type = "object",
                              properties = new { message = new { type = "string" } },
                              required = new[] { "message" },
                              additionalProperties = false,
                          },
                          errors = new
                          {
                              type = "array",
                              items = new
                              {
                                  type = "object",
                                  properties = new
                                  {
                                      code = new { type = "string" },
                                      detail = new { type = "string" },
                                  },
                                  required = new[] { "code", "detail" },
                                  additionalProperties = false,
                              },
                          },
                          metadata = new
                          {
                              type = "object",
                              properties = new { request_id = new { type = "string" } },
                              required = new[] { "request_id" },
                              additionalProperties = false,
                          },
                      }),
                      ["required"] = JsonSerializer.SerializeToElement(new[] { "status", "data", "metadata" }),
                      ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
                  },
              },
          },
      };

      var message = await client.Messages.Create(parameters);
      Console.WriteLine(message);
      ```

      ```go Go
      client := anthropic.NewClient()

      response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
      	Model:     anthropic.ModelClaudeOpus5_5,
      	MaxTokens: 1024,
      	OutputConfig: anthropic.OutputConfigParam{
      		Format: anthropic.JSONOutputFormatParam{
      			Schema: map[string]any{
      				"type":                 "object",
      				"additionalProperties": false,
      				"properties": map[string]any{
      					"status": map[string]any{
      						"type": "string",
      					},
      					"data": map[string]any{
      						"type":                 "object",
      						"additionalProperties": false,
      						"properties": map[string]any{
      							"message": map[string]any{"type": "string"},
      						},
      						"required": []string{"message"},
      					},
      					"errors": map[string]any{
      						"type": "array",
      						"items": map[string]any{
      							"type":                 "object",
      							"additionalProperties": false,
      							"properties": map[string]any{
      								"code":   map[string]any{"type": "string"},
      								"detail": map[string]any{"type": "string"},
      							},
      							"required": []string{"code", "detail"},
      						},
      					},
      					"metadata": map[string]any{
      						"type":                 "object",
      						"additionalProperties": false,
      						"properties": map[string]any{
      							"request_id": map[string]any{"type": "string"},
      						},
      						"required": []string{"request_id"},
      					},
      				},
      				"required": []string{"status", "data", "metadata"},
      			},
      		},
      	},
      	Messages: []anthropic.MessageParam{
      		anthropic.NewUserMessage(anthropic.NewTextBlock(
      			"Write an API response: request req_2abc3def has an invalid email.",
      		)),
      	},
      })
      if err != nil {
      	log.Fatal(err)
      }
      for _, block := range response.Content {
      	switch variant := block.AsAny().(type) {
      	case anthropic.TextBlock:
      		fmt.Println(variant.Text)
      	}
      }
      ```

      ```java Java
      import com.fasterxml.jackson.annotation.JsonProperty;

      static class Payload {
          @JsonProperty("message")
          public String message;
      }

      static class APIError {
          @JsonProperty("code")
          public String code;

          @JsonProperty("detail")
          public String detail;
      }

      static class Metadata {
          @JsonProperty("request_id")
          public String requestId;
      }

      static class APIResponse {
          @JsonProperty("status")
          public String status;

          @JsonProperty("data")
          public Payload data;

          @JsonProperty("errors")
          public List<APIError> errors;

          @JsonProperty("metadata")
          public Metadata metadata;
      }

      void main() {
          AnthropicClient client = AnthropicOkHttpClient.fromEnv();

          StructuredMessageCreateParams<APIResponse> params = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(1024L)
              .outputConfig(APIResponse.class)
              .addUserMessage("Write an API response: request req_2abc3def has an invalid email.")
              .build();

          StructuredMessage<APIResponse> response = client.messages().create(params);
          APIResponse result = response.content().stream()
              .flatMap(block -> block.text().stream())
              .findFirst().orElseThrow().text();
          IO.println(result.status + ": " + result.data.message);
      }
      ```

      ```php PHP
      use Anthropic\Lib\Attributes\Constrained;
      use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
      use Anthropic\Lib\Contracts\StructuredOutputModel;

      $client = new Client();

      class Payload implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          public string $message;
      }

      class APIError implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          public string $code;
          public string $detail;
      }

      class Metadata implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          public string $request_id;
      }

      class APIResponse implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          public string $status;
          public Payload $data;
          #[Constrained(itemClass: APIError::class)]
          public ?array $errors;
          public Metadata $metadata;
      }

      $message = $client->messages->create(
          maxTokens: 1024,
          messages: [
              [
                  'role' => 'user',
                  'content' => 'Write an API response: request req_2abc3def has an invalid email.',
              ],
          ],
          model: 'claude-opus-5-5',
          outputConfig: ['format' => APIResponse::class],
      );

      $result = $message->parsedOutput();
      if ($result instanceof APIResponse) {
          echo "{$result->status}: {$result->data->message}\n";
      }
      ```

      ```ruby Ruby
      client = Anthropic::Client.new

      class Payload < Anthropic::BaseModel
        required :message, String
      end

      class APIError < Anthropic::BaseModel
        required :code, String
        required :detail, String
      end

      class Metadata < Anthropic::BaseModel
        required :request_id, String
      end

      class APIResponse < Anthropic::BaseModel
        required :status, String
        required :data, Payload
        optional :errors, Anthropic::ArrayOf[APIError]
        required :metadata, Metadata
      end

      message = client.messages.create(
        model: Anthropic::Model::CLAUDE_OPUS_5_5,
        max_tokens: 1024,
        output_config: { format: APIResponse },
        messages: [
          { role: "user", content: "Write an API response: request req_2abc3def has an invalid email." }
        ]
      )
      puts message.parsed_output
      ```
    </CodeGroup>
  </Accordion>
</AccordionGroup>

## Uso estricto de herramientas

Para aplicar el cumplimiento de JSON Schema en las entradas de herramientas con “grammar-constrained sampling” (muestreo restringido por gramática), consulta [Uso estricto de herramientas](https://platform.claude.com/docs/es/agents-and-tools/tool-use/strict-tool-use).

### Uso de ambas funciones juntas

Las salidas JSON y el uso estricto de herramientas resuelven problemas diferentes y funcionan juntos:

* Las **salidas JSON** controlan el formato de respuesta de Claude (lo que Claude dice).
* El **uso estricto de herramientas** valida los parámetros de las herramientas (cómo Claude llama a tus funciones).

Cuando se combinan, Claude puede llamar a herramientas con parámetros garantizados como válidos Y devolver respuestas JSON estructuradas. Esto es útil para flujos de trabajo agénticos en los que necesitas tanto llamadas a herramientas confiables como salidas finales estructuradas.

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages \
    -H "content-type: application/json" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2026-10-21" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "messages": [
        {
          "role": "user",
          "content": "Help me plan a trip to Paris departing May 15, 2026"
        }
      ],
      "output_config": {
        "format": {
          "type": "json_schema",
          "schema": {
            "type": "object",
            "properties": {
              "summary": {"type": "string"},
              "next_steps": {"type": "array", "items": {"type": "string"}}
            },
            "required": ["summary", "next_steps"],
            "additionalProperties": false
          }
        }
      },
      "tools": [
        {
          "name": "search_flights",
          "strict": true,
          "input_schema": {
            "type": "object",
            "properties": {
              "destination": {"type": "string"},
              "date": {"type": "string", "format": "date"}
            },
            "required": ["destination", "date"],
            "additionalProperties": false
          }
        }
      ]
    }'
  ```

  ```bash CLI
  ant messages create <<'YAML'
  model: claude-opus-5-5
  max_tokens: 1024
  messages:
    - role: user
      content: Help me plan a trip to Paris departing May 15, 2026
  # Salidas JSON: formato de respuesta estructurado
  output_config:
    format:
      type: json_schema
      schema:
        type: object
        properties:
          summary:
            type: string
          next_steps:
            type: array
            items:
              type: string
        required: [summary, next_steps]
        additionalProperties: false
  # Uso de herramientas estricto: parámetros de herramienta garantizados
  tools:
    - name: search_flights
      strict: true
      input_schema:
        type: object
        properties:
          destination:
            type: string
          date:
            type: string
            format: date
        required: [destination, date]
        additionalProperties: false
  YAML
  ```

  ```python Python
  client = anthropic.Anthropic()

  response = client.messages.create(
      model="claude-opus-5-5",
      max_tokens=1024,
      messages=[
          {
              "role": "user",
              "content": "Help me plan a trip to Paris departing May 15, 2026",
          }
      ],
      # Salidas JSON: formato de respuesta estructurado
      output_config={
          "format": {
              "type": "json_schema",
              "schema": {
                  "type": "object",
                  "properties": {
                      "summary": {"type": "string"},
                      "next_steps": {"type": "array", "items": {"type": "string"}},
                  },
                  "required": ["summary", "next_steps"],
                  "additionalProperties": False,
              },
          }
      },
      # Uso de herramientas estricto: parámetros de herramientas garantizados
      tools=[
          {
              "name": "search_flights",
              "strict": True,
              "input_schema": {
                  "type": "object",
                  "properties": {
                      "destination": {"type": "string"},
                      "date": {"type": "string", "format": "date"},
                  },
                  "required": ["destination", "date"],
                  "additionalProperties": False,
              },
          }
      ],
  )

  print(response)
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  const response = await client.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Help me plan a trip to Paris departing May 15, 2026" }],
    // Salidas JSON: formato de respuesta estructurado
    output_config: {
      format: {
        type: "json_schema",
        schema: {
          type: "object",
          properties: {
            summary: { type: "string" },
            next_steps: { type: "array", items: { type: "string" } }
          },
          required: ["summary", "next_steps"],
          additionalProperties: false
        }
      }
    },
    // Uso estricto de herramientas: parámetros de herramienta garantizados
    tools: [
      {
        name: "search_flights",
        description: "Search for available flights to a destination on a specific date",
        strict: true,
        input_schema: {
          type: "object",
          properties: {
            destination: { type: "string" },
            date: { type: "string", format: "date" }
          },
          required: ["destination", "date"],
          additionalProperties: false
        }
      }
    ]
  });

  // Claude podría llamar primero a la herramienta (tool_use) o responder con JSON (text)
  console.log("Stop reason:", response.stop_reason);
  for (const block of response.content) {
    switch (block.type) {
      case "tool_use":
        console.log(`Tool call: ${block.name}(${JSON.stringify(block.input)})`);
        break;
      case "text":
        console.log("Response:", block.text);
        break;
    }
  }
  ```

  ```csharp C#
  AnthropicClient client = new();

  var parameters = new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Messages = [new UserMessageParam { Content = "Help me plan a trip to Paris departing May 15, 2026" }],
      // Salidas JSON: formato de respuesta estructurado
      OutputConfig = new OutputConfig
      {
          Format = new JsonOutputFormat
          {
              Schema = new Dictionary<string, JsonElement>
              {
                  ["type"] = JsonSerializer.SerializeToElement("object"),
                  ["properties"] = JsonSerializer.SerializeToElement(new
                  {
                      summary = new { type = "string" },
                      next_steps = new { type = "array", items = new { type = "string" } },
                  }),
                  ["required"] = JsonSerializer.SerializeToElement(new[] { "summary", "next_steps" }),
                  ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
              },
          },
      },
      // Uso estricto de herramientas: parámetros de herramientas garantizados
      Tools =
      [
          new Tool
          {
              Name = "search_flights",
              Strict = true,
              InputSchema = new InputSchema(new Dictionary<string, JsonElement>
              {
                  ["properties"] = JsonSerializer.SerializeToElement(new Dictionary<string, object>
                  {
                      ["destination"] = new { type = "string" },
                      ["date"] = new { type = "string", format = "date" },
                  }),
                  ["required"] = JsonSerializer.SerializeToElement(new[] { "destination", "date" }),
                  ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
              }),
          }
      ],
  };

  var message = await client.Messages.Create(parameters);
  Console.WriteLine(message);
  ```

  ```go Go
  client := anthropic.NewClient()

  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Help me plan a trip to Paris departing May 15, 2026")),
  	},
  	// Salidas JSON: formato de respuesta estructurado
  	OutputConfig: anthropic.OutputConfigParam{
  		Format: anthropic.JSONOutputFormatParam{
  			Schema: map[string]any{
  				"type":                 "object",
  				"additionalProperties": false,
  				"properties": map[string]any{
  					"summary":    map[string]any{"type": "string"},
  					"next_steps": map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
  				},
  				"required": []string{"summary", "next_steps"},
  			},
  		},
  	},
  	// Uso de herramientas estricto: parámetros de herramientas garantizados
  	Tools: []anthropic.ToolUnionParam{
  		{OfTool: &anthropic.ToolParam{
  			Name:   "search_flights",
  			Strict: anthropic.Bool(true),
  			InputSchema: anthropic.ToolInputSchemaParam{
  				Properties: map[string]any{
  					"destination": map[string]any{"type": "string"},
  					"date":        map[string]any{"type": "string", "format": "date"},
  				},
  				Required: []string{"destination", "date"},
  				ExtraFields: map[string]any{
  					"additionalProperties": false,
  				},
  			}}},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(response.Content)
  ```

  ```java Java
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  // Salidas JSON: formato de respuesta estructurado
  JsonOutputFormat.Schema outputSchema = JsonOutputFormat.Schema.builder()
      .putAdditionalProperty("type", JsonValue.from("object"))
      .putAdditionalProperty("properties", JsonValue.from(Map.of(
          "summary", Map.of("type", "string"),
          "next_steps", Map.of("type", "array", "items", Map.of("type", "string"))
      )))
      .putAdditionalProperty("required", JsonValue.from(List.of("summary", "next_steps")))
      .putAdditionalProperty("additionalProperties", JsonValue.from(false))
      .build();

  // Uso estricto de herramientas: parámetros de herramientas garantizados
  InputSchema toolSchema = InputSchema.builder()
      .properties(JsonValue.from(Map.of(
          "destination", Map.of("type", "string"),
          "date", Map.of("type", "string", "format", "date")
      )))
      .putAdditionalProperty("required", JsonValue.from(List.of("destination", "date")))
      .putAdditionalProperty("additionalProperties", JsonValue.from(false))
      .build();

  MessageCreateParams params = MessageCreateParams.builder()
      .model(Model.CLAUDE_OPUS_5_5)
      .maxTokens(1024L)
      .addUserMessage("Help me plan a trip to Paris departing May 15, 2026")
      .outputConfig(OutputConfig.builder()
          .format(JsonOutputFormat.builder()
              .schema(outputSchema)
              .build())
          .build())
      .addTool(Tool.builder()
          .name("search_flights")
          .description("Search for available flights to a destination on a specific date")
          .strict(true)
          .inputSchema(toolSchema)
          .build())
      .build();

  Message response = client.messages().create(params);
  IO.println(response);
  ```

  ```php PHP
  use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
  use Anthropic\Lib\Contracts\StructuredOutputModel;
  use Anthropic\Messages\ToolUseBlock;

  $client = new Client();

  class TripPlan implements StructuredOutputModel
  {
      use StructuredOutputModelTrait;

      public string $summary;
      public array $next_steps;
  }

  $message = $client->messages->create(
      maxTokens: 1024,
      messages: [
          ['role' => 'user', 'content' => 'Help me plan a trip to Paris departing May 15, 2026']
      ],
      model: 'claude-opus-5-5',
      // Salidas JSON: formato de respuesta estructurado
      outputConfig: ['format' => TripPlan::class],
      // Uso estricto de herramientas: parámetros de herramienta garantizados
      tools: [
          [
              'name' => 'search_flights',
              'strict' => true,
              'input_schema' => [
                  'type' => 'object',
                  'properties' => [
                      'destination' => ['type' => 'string'],
                      'date' => ['type' => 'string', 'format' => 'date']
                  ],
                  'required' => ['destination', 'date'],
                  'additionalProperties' => false
              ]
          ]
      ],
  );

  // Claude podría llamar primero a la herramienta (tool_use) o responder con JSON (text)
  $plan = $message->parsedOutput();
  if ($plan instanceof TripPlan) {
      echo $plan->summary, "\n";
  } elseif ($toolUse = array_find($message->content, fn($block) => $block instanceof ToolUseBlock)) {
      echo "Tool call: {$toolUse->name}(", json_encode($toolUse->input), ")\n";
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  message = client.messages.create(
    model: Anthropic::Model::CLAUDE_OPUS_5_5,
    max_tokens: 1024,
    messages: [
      { role: "user", content: "Help me plan a trip to Paris departing May 15, 2026" }
    ],
    # Salidas JSON: formato de respuesta estructurado
    output_config: {
      format: {
        type: :json_schema,
        schema: {
          type: "object",
          properties: {
            summary: { type: "string" },
            next_steps: { type: "array", items: { type: "string" } }
          },
          required: ["summary", "next_steps"],
          additionalProperties: false
        }
      }
    },
    # Uso estricto de herramientas: parámetros de herramienta garantizados
    tools: [
      {
        name: "search_flights",
        strict: true,
        input_schema: {
          type: "object",
          properties: {
            destination: { type: "string" },
            date: { type: "string", format: "date" }
          },
          required: ["destination", "date"],
          additionalProperties: false
        }
      }
    ]
  )
  puts message
  ```
</CodeGroup>

## Consideraciones importantes

### Compilación y almacenamiento en caché de gramáticas

Las salidas estructuradas usan muestreo restringido con artefactos de gramática compilados. Esto introduce algunas características de rendimiento que debes tener en cuenta:

* **Latencia de la primera solicitud:** La primera vez que usas un esquema específico, hay “latency” (latencia) adicional mientras se compila la gramática.
* **Almacenamiento en caché automático:** Las gramáticas compiladas se almacenan en caché durante 24 horas desde el último uso, lo que hace que las solicitudes posteriores sean mucho más rápidas.
* **Invalidación de la caché:** La caché se invalida si cambias la estructura del esquema JSON o, al usar tanto salidas estructuradas como uso de herramientas, el conjunto de herramientas de tu solicitud. Cambiar solo los campos `name` o `description` no invalida la caché.

### Modificación del prompt y costos de tokens

Al usar salidas estructuradas, Claude recibe automáticamente una “system prompt” (indicación del sistema) adicional que explica el formato de salida esperado. Esto significa que:

* Tu recuento de tokens de entrada es ligeramente mayor.
* El prompt inyectado te cuesta tokens como cualquier otra indicación del sistema.
* Cambiar el parámetro `output_config.format` invalida cualquier [caché de prompts](https://platform.claude.com/docs/es/build-with-claude/prompt-caching) para ese hilo de conversación.

### Limitaciones de JSON Schema

Las salidas estructuradas admiten JSON Schema estándar con algunas limitaciones. Tanto las salidas JSON como el uso estricto de herramientas comparten estas limitaciones.

<Accordion title="Funciones compatibles">
  * Todos los tipos básicos: object, array, string, integer, number, boolean, null
  * `enum` (solo cadenas, números, booleanos o nulls, sin tipos complejos). Para una advertencia sobre el uso de mayúsculas, consulta [Salidas no válidas](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#invalid-outputs).
  * `const`
  * `anyOf` y `allOf` (con limitaciones: `allOf` con `$ref` no es compatible)
  * `$ref`, `$def` y `definitions` (`$ref` externo no es compatible)
  * Propiedad `default` para todos los tipos compatibles
  * `required` y `additionalProperties` (debe establecerse en `false` para los objetos)
  * Formatos de cadena: `date-time`, `time`, `date`, `duration`, `email`, `hostname`, `uri`, `ipv4`, `ipv6`, `uuid`
  * `minItems` de arrays (solo se admiten los valores 0 y 1)
</Accordion>

<Accordion title="No compatible">
  * Esquemas recursivos
  * Tipos complejos dentro de enums
  * `$ref` externo (por ejemplo, `'$ref': 'http://...'`)
  * Restricciones numéricas (como `minimum`, `maximum`, `multipleOf`)
  * Restricciones de cadena (`minLength`, `maxLength`)
  * Restricciones de arrays más allá de `minItems` de 0 o 1
  * `additionalProperties` establecido en cualquier valor distinto de `false` (consulta [Objetos con claves arbitrarias](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#objects-with-arbitrary-keys))

  Si usas una función no compatible, recibirás un error 400 con detalles.
</Accordion>

<Accordion title="Compatibilidad con patrones (regex)">
  **Funciones de regex compatibles:**

  * Coincidencia completa (`^...$`) y coincidencia parcial
  * Cuantificadores: `*`, `+`, `?`, casos simples de `{n,m}`
  * Clases de caracteres: `[]`, `.`, `\d`, `\w`, `\s`
  * Grupos: `(...)`

  **NO compatibles:**

  * Referencias inversas a grupos (por ejemplo, `\1`, `\2`)
  * Aserciones lookahead/lookbehind (por ejemplo, `(?=...)`, `(?!...)`)
  * Límites de palabra: `\b`, `\B`
  * Cuantificadores `{n,m}` complejos con rangos grandes

  Los patrones de regex simples funcionan bien. Los patrones complejos podrían provocar errores 400.
</Accordion>

<Tip>
  Los helpers del SDK pueden transformar esquemas con [restricciones no compatibles con la API](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#json-schema-limitations). Consulta [Cómo funciona la transformación del SDK](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#how-sdk-transformation-works).
</Tip>

#### Objetos con claves arbitrarias

Enumera cada clave de un objeto, ya sea en `properties` o como un campo de la clase o del esquema que pasas al SDK. Como `additionalProperties` debe ser `false`, Claude solo puede devolver las claves que enumeres.

La API acepta un objeto con `properties` vacío o ausente, pero Claude solo puede devolver `{}` para él. Un tipo map, diccionario o record no enumera ninguna clave, por lo que los SDK de Python y TypeScript lo envían como un objeto con `properties` vacío. Los SDK de Java y Ruby generan un error en su lugar.

Si no conoces las claves de antemano, usa un array en su lugar. Dale a cada elemento una `key` y un `value`, como en este esquema:

```json
{
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "key": { "type": "string" },
      "value": { "type": "string" }
    },
    "required": ["key", "value"],
    "additionalProperties": false
  }
}
```

### Orden de las propiedades

Al usar salidas estructuradas, las propiedades de los objetos mantienen el orden definido en tu esquema, con una advertencia importante: **las propiedades obligatorias aparecen primero, seguidas de las propiedades opcionales**.

Por ejemplo, dado este esquema:

```json
{
  "type": "object",
  "properties": {
    "notes": { "type": "string" },
    "name": { "type": "string" },
    "email": { "type": "string" },
    "age": { "type": "integer" }
  },
  "required": ["name", "email"],
  "additionalProperties": false
}
```

La salida ordena las propiedades así:

1. `name` (obligatoria, en el orden del esquema)
2. `email` (obligatoria, en el orden del esquema)
3. `notes` (opcional, en el orden del esquema)
4. `age` (opcional, en el orden del esquema)

Esto significa que la salida podría verse así:

```json
{
  "name": "John Smith",
  "email": "john@example.com",
  "notes": "Interested in enterprise plan",
  "age": 35
}
```

Si el orden de las propiedades en la salida es importante para tu aplicación, marca todas las propiedades como obligatorias o ten en cuenta este reordenamiento en tu lógica de análisis.

### Salidas no válidas

Aunque las salidas estructuradas garantizan el cumplimiento del esquema en la mayoría de los casos, hay escenarios en los que la salida podría no coincidir con tu esquema:

**Rechazos** (`stop_reason: "refusal"`)

Claude mantiene sus propiedades de seguridad y utilidad incluso al usar salidas estructuradas. Si Claude rechaza una solicitud por motivos de seguridad:

* La respuesta tiene `stop_reason: "refusal"`.
* Recibirás un código de estado 200.
* Se te facturarán los tokens generados.
* La salida podría no coincidir con tu esquema porque el mensaje de rechazo tiene prioridad sobre las restricciones del esquema.

Una propiedad que pida el pensamiento o el razonamiento paso a paso del modelo podría provocar un rechazo `reasoning_extraction`. En su lugar, pide una explicación breve. Consulta [Mantén el razonamiento en los bloques de pensamiento](https://platform.claude.com/docs/es/build-with-claude/refusals-and-fallback#keep-reasoning-in-thinking-blocks).

**Límite de tokens alcanzado** (`stop_reason: "max_tokens"`)

Si la respuesta se corta porque alcanzó el límite de `max_tokens`:

* La respuesta tiene `stop_reason: "max_tokens"`.
* La salida podría estar incompleta y no coincidir con tu esquema.
* Vuelve a intentarlo con un valor de `max_tokens` más alto para obtener la salida estructurada completa.

**Uso de mayúsculas en los valores enum**

Las salidas estructuradas no garantizan el uso de mayúsculas en los valores de cadena `enum` y `const`: Claude podría devolver un valor que difiere de tu esquema solo en el uso de mayúsculas, normalmente en la primera letra de una palabra que sigue a un espacio. Por ejemplo, dado este esquema:

```json
{
  "type": "string",
  "enum": ["Conversation Topic 1", "Conversation Topic 2", "Conversation topic 3"]
}
```

La salida podría contener `"Conversation Topic 3"` (“T” mayúscula) aunque ese valor exacto no esté en el enum. La respuesta se completa normalmente, sin error y sin un `stop_reason` especial. Esto se aplica tanto a las salidas JSON como al uso estricto de herramientas. Compara los valores enum sin distinguir entre mayúsculas y minúsculas, y evita valores enum que difieran solo en el uso de mayúsculas.

### Límites de complejidad del esquema

Las salidas estructuradas funcionan compilando tus esquemas JSON en una gramática que restringe la salida de Claude. Los esquemas más complejos producen gramáticas más grandes que tardan más en compilarse. Para protegerse contra tiempos de compilación excesivos, la API aplica varios límites de complejidad.

#### Límites explícitos

Los siguientes límites se aplican a todas las solicitudes con `output_config.format` o `strict: true`:

| Límite                               | Valor | Descripción                                                                                                                                                                                                        |
| ------------------------------------ | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Herramientas estrictas por solicitud | 20    | Número máximo de herramientas con `strict: true`. Las herramientas no estrictas no cuentan para este límite.                                                                                                       |
| Parámetros opcionales                | 24    | Total de parámetros opcionales en todos los esquemas de herramientas estrictas y esquemas de salida JSON. Cada parámetro que no aparece en `required` cuenta para este límite.                                     |
| Parámetros con tipos unión           | 16    | Total de parámetros que usan `anyOf` o arrays de tipos (por ejemplo, `"type": ["string", "null"]`) en todos los esquemas estrictos. Son especialmente costosos porque generan un costo de compilación exponencial. |

<Note>
  Estos límites se aplican al total combinado de todos los esquemas estrictos en una sola solicitud. Por ejemplo, si tienes cuatro herramientas estrictas con seis parámetros opcionales cada una, alcanzarás el límite de 24 parámetros aunque ninguna herramienta individual parezca compleja.
</Note>

#### Límites internos adicionales

Además de los límites explícitos de la tabla anterior, existen límites internos adicionales sobre el tamaño de la gramática compilada. Estos límites existen porque la complejidad del esquema no se reduce a una sola dimensión: funciones como los parámetros opcionales, los tipos unión, los objetos anidados y el número de herramientas interactúan entre sí de maneras que pueden hacer que la gramática compilada sea desproporcionadamente grande.

Cuando se superan estos límites, recibirás un error 400 con el mensaje “Schema is too complex for compilation.” Estos errores significan que la complejidad combinada de tus esquemas supera lo que se puede compilar de forma eficiente, incluso si se cumple cada límite individual de la tabla anterior. Como último recurso, la API también aplica un **tiempo de espera de compilación de 180 segundos**. Los esquemas que pasan todas las comprobaciones explícitas pero producen gramáticas compiladas muy grandes podrían alcanzar este tiempo de espera.

#### Consejos para reducir la complejidad del esquema

Si estás alcanzando los límites de complejidad, prueba estas estrategias en orden:

1. **Marca como estrictas solo las herramientas críticas.** Si tienes muchas herramientas, reserva el uso estricto de herramientas para aquellas en las que las infracciones del esquema causan problemas reales, y confía en la adherencia natural de Claude para las herramientas más simples.

2. **Reduce los parámetros opcionales.** Haz que los parámetros sean `required` siempre que sea posible. Cada parámetro opcional duplica aproximadamente una parte del espacio de estados de la gramática. Si un parámetro siempre tiene un valor predeterminado razonable, considera hacerlo obligatorio y que Claude proporcione ese valor predeterminado de forma explícita.

3. **Simplifica las estructuras anidadas.** Los objetos profundamente anidados con campos opcionales agravan la complejidad. Aplana las estructuras siempre que sea posible.

4. **Divide en varias solicitudes.** Si tienes muchas herramientas estrictas, considera dividirlas en solicitudes separadas o en subagentes.

Para problemas persistentes con esquemas válidos, [contacta con soporte](https://support.claude.com/en/articles/9015913-how-to-get-support) con la definición de tu esquema.

## Migración desde la beta

El parámetro `output_format` se ha movido a `output_config.format`, y los encabezados beta ya no son necesarios. El parámetro `output_format` está obsoleto. Para usarlo de todos modos, agrega el encabezado beta `structured-outputs-2025-11-13`. Sin él, la API devuelve un error 400.

En el SDK de Python (v1.0 y posteriores), un diccionario de esquema pasado como `output_format={...}` genera un `TypeError`. Usa `output_config` en su lugar. Un modelo de Pydantic pasado como `output_format=Model` a `parse()` o `stream()` funciona sin cambios y sin encabezado beta, porque el SDK lo envía como `output_config`. Consulta [Uso de un esquema JSON sin procesar](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#using-a-raw-json-schema) para ver la forma actualizada de la API.

## Compatibilidad de funciones

**Funciona con:**

* **[Procesamiento por lotes](https://platform.claude.com/docs/es/build-with-claude/batch-processing):** Procesa salidas estructuradas a escala con un descuento del 50 por ciento
* **[Conteo de tokens](https://platform.claude.com/docs/es/build-with-claude/token-counting):** Cuenta tokens sin compilación
* **[Streaming](https://platform.claude.com/docs/es/build-with-claude/streaming):** Haz streaming de salidas estructuradas como respuestas normales
* **[Uso combinado](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#using-both-features-together):** Usa salidas JSON (`output_config.format`) y uso estricto de herramientas (`strict: true`) juntos en la misma solicitud

**Incompatible con:**

* **[Citas](https://platform.claude.com/docs/es/build-with-claude/citations):** Las citas requieren intercalar bloques de citas con texto, lo que entra en conflicto con las restricciones estrictas del esquema JSON. La API devuelve un error 400 si las citas están habilitadas con `output_config.format`. Para extraer datos estructurados con una cita para cada valor, consulta [Obtener datos estructurados con citas](https://platform.claude.com/docs/es/build-with-claude/citations#get-structured-data-with-citations).
* **Prellenado de mensajes:** El prellenado es incompatible con las salidas JSON.

<Tip>
  **Alcance de la gramática:** Las gramáticas se aplican solo a la salida directa de Claude, no a las llamadas de uso de herramientas, los resultados de herramientas ni las etiquetas de pensamiento (al usar [pensamiento](https://platform.claude.com/docs/es/build-with-claude/thinking)). El estado de la gramática se restablece entre secciones, lo que permite que Claude piense libremente mientras sigue produciendo una salida estructurada en la respuesta final.
</Tip>

## Disponibilidad

Estado de Salidas estructuradas: disponible en todos los modelos, con elegibilidad para preparación para HIPAA.

### Compatibilidad con la API

#### [Messages API](https://platform.claude.com/docs/es/build-with-claude/working-with-messages)

* Disponibilidad: Disponible

| Estado     | Plataformas en la nube            | Modelos                                                                  |
| ---------- | --------------------------------- | ------------------------------------------------------------------------ |
| Disponible | Todas las plataformas compatibles | [Todos los modelos](https://platform.claude.com/docs/es/models/overview) |

#### [Managed Agents](https://platform.claude.com/docs/es/managed-agents/overview)

* Disponibilidad: No disponible

### Manejo de datos

[Descripción general del manejo de datos](https://platform.claude.com/docs/es/manage-claude/api-and-data-retention#feature-eligibility)

| Elemento | Retención de datos cero                                                                                                                                                                                                                                                                                                                                                             | HIPAA                                                                                                                                                                                                                     |
| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Estado   | Elegible (con condiciones; excluye [Modelos cubiertos](https://platform.claude.com/docs/es/manage-claude/api-and-data-retention#model-specific-data-retention-requirements))                                                                                                                                                                                                        | Elegible                                                                                                                                                                                                                  |
| Detalles | Tus prompts y las salidas de Claude no se almacenan. Solo se almacena en caché el JSON Schema, hasta 24 horas desde su último uso. Esto también abarca el uso estricto de herramientas (`strict: true` en las herramientas), que usa la misma canalización de gramática. [Más información](https://platform.claude.com/docs/es/build-with-claude/structured-outputs#data-retention) | No se debe incluir PHI en las definiciones de JSON Schema; consulta las directrices de manejo de PHI. [Más información](https://platform.claude.com/docs/es/manage-claude/api-and-data-retention#phi-handling-guidelines) |

Endpoints aplicables:

* `/v1/messages`

Los prompts y las respuestas se procesan con ZDR al usar salidas estructuradas. Sin embargo, el esquema JSON en sí se almacena temporalmente en caché durante un máximo de 24 horas desde el último uso con fines de optimización. No se retienen datos de prompts ni de respuestas más allá de la respuesta de la API.

Las salidas estructuradas son aptas para HIPAA, pero **no se debe incluir PHI en las definiciones de esquemas JSON**. La API compila los esquemas JSON en gramáticas que se almacenan en caché por separado del contenido de los mensajes, y estos esquemas en caché no reciben las mismas protecciones de PHI que los prompts y las respuestas. No incluyas PHI en los nombres de propiedades del esquema, los valores `enum`, los valores `const` ni las expresiones regulares `pattern`. La PHI solo debe aparecer en el contenido de los mensajes (prompts y respuestas), donde está protegida por las salvaguardas de HIPAA.

## Próximos pasos

<CardGroup cols={2}>
  <Card title="Citas" icon="book-bookmark" href="https://platform.claude.com/docs/es/build-with-claude/citations">
    Haz que Claude cite sus fuentes al responder preguntas sobre los documentos proporcionados.
  </Card>

  <Card title="Uso estricto de herramientas" icon="check" href="https://platform.claude.com/docs/es/agents-and-tools/tool-use/strict-tool-use">
    Aplica el cumplimiento de JSON Schema en las entradas de herramientas de Claude con muestreo restringido por gramática.
  </Card>

  <Card title="Uso de herramientas con Claude" icon="wrench" href="https://platform.claude.com/docs/es/agents-and-tools/tool-use/overview">
    Conecta Claude a herramientas y API externas. Aprende dónde se ejecutan las herramientas y cómo funciona el bucle agéntico.
  </Card>

  <Card title="Precios" icon="calculator" href="https://platform.claude.com/docs/es/about-claude/pricing">
    Conoce la estructura de precios de Anthropic para modelos y funciones.
  </Card>
</CardGroup>
