From a142654709477e078308e2e21889f7f8ecbfe79a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2026 21:34:05 +0000 Subject: [PATCH 01/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index bee881e8..75698083 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-05a30711e18b0023520a660352d75595a050d1299bf0e3ee4a8cf55ded36aea2.yml -openapi_spec_hash: 8d0e1115a7d864f27c55cec3255d1e77 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-1a8a7dda98ab9f3c537f46c6192baed039be8f5680d3b9cc2cc227d5b06059ea.yml +openapi_spec_hash: 286076163f3b5111b915830dd21cc469 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 9213463afaf5d76d3b706a5aef49082c72409189 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 5 Apr 2026 03:10:08 +0000 Subject: [PATCH 02/82] feat(api): api update --- .stats.yml | 4 +- src/brand/dev/resources/brand.py | 786 +++++++++++++++++- .../brand_identify_from_transaction_params.py | 65 ++ ...rand_identify_from_transaction_response.py | 130 +++ .../types/brand_retrieve_by_email_params.py | 65 ++ .../types/brand_retrieve_by_email_response.py | 130 +++ .../types/brand_retrieve_by_isin_params.py | 65 ++ .../types/brand_retrieve_by_isin_response.py | 130 +++ .../types/brand_retrieve_by_name_params.py | 65 ++ .../types/brand_retrieve_by_name_response.py | 130 +++ .../types/brand_retrieve_by_ticker_params.py | 65 ++ .../brand_retrieve_by_ticker_response.py | 130 +++ src/brand/dev/types/brand_retrieve_params.py | 70 +- .../dev/types/brand_retrieve_response.py | 130 +++ tests/api_resources/test_brand.py | 24 +- 15 files changed, 1967 insertions(+), 22 deletions(-) diff --git a/.stats.yml b/.stats.yml index 75698083..7c16337f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-1a8a7dda98ab9f3c537f46c6192baed039be8f5680d3b9cc2cc227d5b06059ea.yml -openapi_spec_hash: 286076163f3b5111b915830dd21cc469 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-095758df21a0bad753cde157cc0216339f77aec5b01de9bf36d55e34770cb611.yml +openapi_spec_hash: 4d5456700d25c12524ca030bb93d4261 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index 856a4904..bda4613f 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -89,61 +89,126 @@ def retrieve( *, domain: str, force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] | Omit = omit, max_speed: bool | Omit = omit, @@ -163,8 +228,7 @@ def retrieve( domain: Domain name to retrieve brand data for (e.g., 'example.com', 'google.com'). Cannot be used with name or ticker parameters. - force_language: Optional parameter to force the language of the retrieved brand data. Works with - all three lookup methods. + force_language: Optional parameter to force the language of the retrieved brand data. max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of @@ -711,61 +775,126 @@ def identify_from_transaction( ] | Omit = omit, force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] | Omit = omit, high_confidence_only: bool | Omit = omit, @@ -949,61 +1078,126 @@ def retrieve_by_email( *, email: str, force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] | Omit = omit, max_speed: bool | Omit = omit, @@ -1069,61 +1263,126 @@ def retrieve_by_isin( *, isin: str, force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] | Omit = omit, max_speed: bool | Omit = omit, @@ -1430,61 +1689,126 @@ def retrieve_by_name( ] | Omit = omit, force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] | Omit = omit, max_speed: bool | Omit = omit, @@ -1552,61 +1876,126 @@ def retrieve_by_ticker( *, ticker: str, force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] | Omit = omit, max_speed: bool | Omit = omit, @@ -2189,61 +2578,126 @@ async def retrieve( *, domain: str, force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] | Omit = omit, max_speed: bool | Omit = omit, @@ -2263,8 +2717,7 @@ async def retrieve( domain: Domain name to retrieve brand data for (e.g., 'example.com', 'google.com'). Cannot be used with name or ticker parameters. - force_language: Optional parameter to force the language of the retrieved brand data. Works with - all three lookup methods. + force_language: Optional parameter to force the language of the retrieved brand data. max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of @@ -2811,61 +3264,126 @@ async def identify_from_transaction( ] | Omit = omit, force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] | Omit = omit, high_confidence_only: bool | Omit = omit, @@ -3049,61 +3567,126 @@ async def retrieve_by_email( *, email: str, force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] | Omit = omit, max_speed: bool | Omit = omit, @@ -3169,61 +3752,126 @@ async def retrieve_by_isin( *, isin: str, force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] | Omit = omit, max_speed: bool | Omit = omit, @@ -3530,61 +4178,126 @@ async def retrieve_by_name( ] | Omit = omit, force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] | Omit = omit, max_speed: bool | Omit = omit, @@ -3652,61 +4365,126 @@ async def retrieve_by_ticker( *, ticker: str, force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] | Omit = omit, max_speed: bool | Omit = omit, diff --git a/src/brand/dev/types/brand_identify_from_transaction_params.py b/src/brand/dev/types/brand_identify_from_transaction_params.py index e04b1e53..bd3f5a8f 100644 --- a/src/brand/dev/types/brand_identify_from_transaction_params.py +++ b/src/brand/dev/types/brand_identify_from_transaction_params.py @@ -263,61 +263,126 @@ class BrandIdentifyFromTransactionParams(TypedDict, total=False): """ force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] """Optional parameter to force the language of the retrieved brand data.""" diff --git a/src/brand/dev/types/brand_identify_from_transaction_response.py b/src/brand/dev/types/brand_identify_from_transaction_response.py index 5c1261dd..866c25f4 100644 --- a/src/brand/dev/types/brand_identify_from_transaction_response.py +++ b/src/brand/dev/types/brand_identify_from_transaction_response.py @@ -470,6 +470,136 @@ class Brand(BaseModel): phone: Optional[str] = None """Company phone number""" + primary_language: Optional[ + Literal[ + "afrikaans", + "albanian", + "amharic", + "arabic", + "armenian", + "assamese", + "aymara", + "azeri", + "basque", + "belarusian", + "bengali", + "bosnian", + "bulgarian", + "burmese", + "cantonese", + "catalan", + "cebuano", + "chinese", + "corsican", + "croatian", + "czech", + "danish", + "dutch", + "english", + "esperanto", + "estonian", + "farsi", + "fijian", + "finnish", + "french", + "galician", + "georgian", + "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", + "hausa", + "hawaiian", + "hebrew", + "hindi", + "hmong", + "hungarian", + "icelandic", + "igbo", + "indonesian", + "irish", + "italian", + "japanese", + "javanese", + "kannada", + "kazakh", + "khmer", + "kinyarwanda", + "korean", + "kurdish", + "kyrgyz", + "lao", + "latin", + "latvian", + "lingala", + "lithuanian", + "luxembourgish", + "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", + "mongolian", + "nepali", + "norwegian", + "odia", + "oromo", + "pashto", + "pidgin", + "polish", + "portuguese", + "punjabi", + "quechua", + "romanian", + "russian", + "samoan", + "scottish-gaelic", + "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", + "slovak", + "slovene", + "somali", + "spanish", + "sundanese", + "swahili", + "swedish", + "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", + "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", + "turkish", + "turkmen", + "ukrainian", + "urdu", + "uyghur", + "uzbek", + "vietnamese", + "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", + ] + ] = None + """The primary language of the brand's website content. + + Detected from the HTML lang tag, page content analysis, or social media + descriptions. + """ + slogan: Optional[str] = None """The brand's slogan""" diff --git a/src/brand/dev/types/brand_retrieve_by_email_params.py b/src/brand/dev/types/brand_retrieve_by_email_params.py index 886c2135..84949e7b 100644 --- a/src/brand/dev/types/brand_retrieve_by_email_params.py +++ b/src/brand/dev/types/brand_retrieve_by_email_params.py @@ -18,61 +18,126 @@ class BrandRetrieveByEmailParams(TypedDict, total=False): """ force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] """Optional parameter to force the language of the retrieved brand data.""" diff --git a/src/brand/dev/types/brand_retrieve_by_email_response.py b/src/brand/dev/types/brand_retrieve_by_email_response.py index 922fa75b..f7bb8e18 100644 --- a/src/brand/dev/types/brand_retrieve_by_email_response.py +++ b/src/brand/dev/types/brand_retrieve_by_email_response.py @@ -470,6 +470,136 @@ class Brand(BaseModel): phone: Optional[str] = None """Company phone number""" + primary_language: Optional[ + Literal[ + "afrikaans", + "albanian", + "amharic", + "arabic", + "armenian", + "assamese", + "aymara", + "azeri", + "basque", + "belarusian", + "bengali", + "bosnian", + "bulgarian", + "burmese", + "cantonese", + "catalan", + "cebuano", + "chinese", + "corsican", + "croatian", + "czech", + "danish", + "dutch", + "english", + "esperanto", + "estonian", + "farsi", + "fijian", + "finnish", + "french", + "galician", + "georgian", + "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", + "hausa", + "hawaiian", + "hebrew", + "hindi", + "hmong", + "hungarian", + "icelandic", + "igbo", + "indonesian", + "irish", + "italian", + "japanese", + "javanese", + "kannada", + "kazakh", + "khmer", + "kinyarwanda", + "korean", + "kurdish", + "kyrgyz", + "lao", + "latin", + "latvian", + "lingala", + "lithuanian", + "luxembourgish", + "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", + "mongolian", + "nepali", + "norwegian", + "odia", + "oromo", + "pashto", + "pidgin", + "polish", + "portuguese", + "punjabi", + "quechua", + "romanian", + "russian", + "samoan", + "scottish-gaelic", + "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", + "slovak", + "slovene", + "somali", + "spanish", + "sundanese", + "swahili", + "swedish", + "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", + "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", + "turkish", + "turkmen", + "ukrainian", + "urdu", + "uyghur", + "uzbek", + "vietnamese", + "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", + ] + ] = None + """The primary language of the brand's website content. + + Detected from the HTML lang tag, page content analysis, or social media + descriptions. + """ + slogan: Optional[str] = None """The brand's slogan""" diff --git a/src/brand/dev/types/brand_retrieve_by_isin_params.py b/src/brand/dev/types/brand_retrieve_by_isin_params.py index db559fae..5be121e4 100644 --- a/src/brand/dev/types/brand_retrieve_by_isin_params.py +++ b/src/brand/dev/types/brand_retrieve_by_isin_params.py @@ -18,61 +18,126 @@ class BrandRetrieveByIsinParams(TypedDict, total=False): """ force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] """Optional parameter to force the language of the retrieved brand data.""" diff --git a/src/brand/dev/types/brand_retrieve_by_isin_response.py b/src/brand/dev/types/brand_retrieve_by_isin_response.py index 21a860e4..4e15e34f 100644 --- a/src/brand/dev/types/brand_retrieve_by_isin_response.py +++ b/src/brand/dev/types/brand_retrieve_by_isin_response.py @@ -470,6 +470,136 @@ class Brand(BaseModel): phone: Optional[str] = None """Company phone number""" + primary_language: Optional[ + Literal[ + "afrikaans", + "albanian", + "amharic", + "arabic", + "armenian", + "assamese", + "aymara", + "azeri", + "basque", + "belarusian", + "bengali", + "bosnian", + "bulgarian", + "burmese", + "cantonese", + "catalan", + "cebuano", + "chinese", + "corsican", + "croatian", + "czech", + "danish", + "dutch", + "english", + "esperanto", + "estonian", + "farsi", + "fijian", + "finnish", + "french", + "galician", + "georgian", + "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", + "hausa", + "hawaiian", + "hebrew", + "hindi", + "hmong", + "hungarian", + "icelandic", + "igbo", + "indonesian", + "irish", + "italian", + "japanese", + "javanese", + "kannada", + "kazakh", + "khmer", + "kinyarwanda", + "korean", + "kurdish", + "kyrgyz", + "lao", + "latin", + "latvian", + "lingala", + "lithuanian", + "luxembourgish", + "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", + "mongolian", + "nepali", + "norwegian", + "odia", + "oromo", + "pashto", + "pidgin", + "polish", + "portuguese", + "punjabi", + "quechua", + "romanian", + "russian", + "samoan", + "scottish-gaelic", + "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", + "slovak", + "slovene", + "somali", + "spanish", + "sundanese", + "swahili", + "swedish", + "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", + "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", + "turkish", + "turkmen", + "ukrainian", + "urdu", + "uyghur", + "uzbek", + "vietnamese", + "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", + ] + ] = None + """The primary language of the brand's website content. + + Detected from the HTML lang tag, page content analysis, or social media + descriptions. + """ + slogan: Optional[str] = None """The brand's slogan""" diff --git a/src/brand/dev/types/brand_retrieve_by_name_params.py b/src/brand/dev/types/brand_retrieve_by_name_params.py index 847bdb3f..31a9eefe 100644 --- a/src/brand/dev/types/brand_retrieve_by_name_params.py +++ b/src/brand/dev/types/brand_retrieve_by_name_params.py @@ -263,61 +263,126 @@ class BrandRetrieveByNameParams(TypedDict, total=False): """ force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] """Optional parameter to force the language of the retrieved brand data.""" diff --git a/src/brand/dev/types/brand_retrieve_by_name_response.py b/src/brand/dev/types/brand_retrieve_by_name_response.py index 1e462e7c..cc242097 100644 --- a/src/brand/dev/types/brand_retrieve_by_name_response.py +++ b/src/brand/dev/types/brand_retrieve_by_name_response.py @@ -470,6 +470,136 @@ class Brand(BaseModel): phone: Optional[str] = None """Company phone number""" + primary_language: Optional[ + Literal[ + "afrikaans", + "albanian", + "amharic", + "arabic", + "armenian", + "assamese", + "aymara", + "azeri", + "basque", + "belarusian", + "bengali", + "bosnian", + "bulgarian", + "burmese", + "cantonese", + "catalan", + "cebuano", + "chinese", + "corsican", + "croatian", + "czech", + "danish", + "dutch", + "english", + "esperanto", + "estonian", + "farsi", + "fijian", + "finnish", + "french", + "galician", + "georgian", + "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", + "hausa", + "hawaiian", + "hebrew", + "hindi", + "hmong", + "hungarian", + "icelandic", + "igbo", + "indonesian", + "irish", + "italian", + "japanese", + "javanese", + "kannada", + "kazakh", + "khmer", + "kinyarwanda", + "korean", + "kurdish", + "kyrgyz", + "lao", + "latin", + "latvian", + "lingala", + "lithuanian", + "luxembourgish", + "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", + "mongolian", + "nepali", + "norwegian", + "odia", + "oromo", + "pashto", + "pidgin", + "polish", + "portuguese", + "punjabi", + "quechua", + "romanian", + "russian", + "samoan", + "scottish-gaelic", + "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", + "slovak", + "slovene", + "somali", + "spanish", + "sundanese", + "swahili", + "swedish", + "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", + "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", + "turkish", + "turkmen", + "ukrainian", + "urdu", + "uyghur", + "uzbek", + "vietnamese", + "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", + ] + ] = None + """The primary language of the brand's website content. + + Detected from the HTML lang tag, page content analysis, or social media + descriptions. + """ + slogan: Optional[str] = None """The brand's slogan""" diff --git a/src/brand/dev/types/brand_retrieve_by_ticker_params.py b/src/brand/dev/types/brand_retrieve_by_ticker_params.py index d3283850..9d1124f8 100644 --- a/src/brand/dev/types/brand_retrieve_by_ticker_params.py +++ b/src/brand/dev/types/brand_retrieve_by_ticker_params.py @@ -17,61 +17,126 @@ class BrandRetrieveByTickerParams(TypedDict, total=False): """ force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] """Optional parameter to force the language of the retrieved brand data.""" diff --git a/src/brand/dev/types/brand_retrieve_by_ticker_response.py b/src/brand/dev/types/brand_retrieve_by_ticker_response.py index 9815a65a..a22f66f9 100644 --- a/src/brand/dev/types/brand_retrieve_by_ticker_response.py +++ b/src/brand/dev/types/brand_retrieve_by_ticker_response.py @@ -470,6 +470,136 @@ class Brand(BaseModel): phone: Optional[str] = None """Company phone number""" + primary_language: Optional[ + Literal[ + "afrikaans", + "albanian", + "amharic", + "arabic", + "armenian", + "assamese", + "aymara", + "azeri", + "basque", + "belarusian", + "bengali", + "bosnian", + "bulgarian", + "burmese", + "cantonese", + "catalan", + "cebuano", + "chinese", + "corsican", + "croatian", + "czech", + "danish", + "dutch", + "english", + "esperanto", + "estonian", + "farsi", + "fijian", + "finnish", + "french", + "galician", + "georgian", + "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", + "hausa", + "hawaiian", + "hebrew", + "hindi", + "hmong", + "hungarian", + "icelandic", + "igbo", + "indonesian", + "irish", + "italian", + "japanese", + "javanese", + "kannada", + "kazakh", + "khmer", + "kinyarwanda", + "korean", + "kurdish", + "kyrgyz", + "lao", + "latin", + "latvian", + "lingala", + "lithuanian", + "luxembourgish", + "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", + "mongolian", + "nepali", + "norwegian", + "odia", + "oromo", + "pashto", + "pidgin", + "polish", + "portuguese", + "punjabi", + "quechua", + "romanian", + "russian", + "samoan", + "scottish-gaelic", + "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", + "slovak", + "slovene", + "somali", + "spanish", + "sundanese", + "swahili", + "swedish", + "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", + "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", + "turkish", + "turkmen", + "ukrainian", + "urdu", + "uyghur", + "uzbek", + "vietnamese", + "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", + ] + ] = None + """The primary language of the brand's website content. + + Detected from the HTML lang tag, page content analysis, or social media + descriptions. + """ + slogan: Optional[str] = None """The brand's slogan""" diff --git a/src/brand/dev/types/brand_retrieve_params.py b/src/brand/dev/types/brand_retrieve_params.py index 03e102aa..d25c4c0b 100644 --- a/src/brand/dev/types/brand_retrieve_params.py +++ b/src/brand/dev/types/brand_retrieve_params.py @@ -17,66 +17,128 @@ class BrandRetrieveParams(TypedDict, total=False): """ force_language: Literal[ + "afrikaans", "albanian", + "amharic", "arabic", + "armenian", + "assamese", + "aymara", "azeri", + "basque", + "belarusian", "bengali", + "bosnian", "bulgarian", + "burmese", "cantonese", + "catalan", "cebuano", + "chinese", + "corsican", "croatian", "czech", "danish", "dutch", "english", + "esperanto", "estonian", "farsi", + "fijian", "finnish", "french", + "galician", + "georgian", "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", "hausa", "hawaiian", + "hebrew", "hindi", + "hmong", "hungarian", "icelandic", + "igbo", "indonesian", + "irish", "italian", + "japanese", + "javanese", + "kannada", "kazakh", + "khmer", + "kinyarwanda", "korean", + "kurdish", "kyrgyz", + "lao", "latin", "latvian", + "lingala", "lithuanian", + "luxembourgish", "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", "mongolian", "nepali", "norwegian", + "odia", + "oromo", "pashto", "pidgin", "polish", "portuguese", + "punjabi", + "quechua", "romanian", "russian", + "samoan", + "scottish-gaelic", "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", "slovak", "slovene", "somali", "spanish", + "sundanese", "swahili", "swedish", "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", "turkish", + "turkmen", "ukrainian", "urdu", + "uyghur", "uzbek", "vietnamese", "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", ] - """Optional parameter to force the language of the retrieved brand data. - - Works with all three lookup methods. - """ + """Optional parameter to force the language of the retrieved brand data.""" max_speed: Annotated[bool, PropertyInfo(alias="maxSpeed")] """Optional parameter to optimize the API call for maximum speed. diff --git a/src/brand/dev/types/brand_retrieve_response.py b/src/brand/dev/types/brand_retrieve_response.py index 28aea6f0..207c631b 100644 --- a/src/brand/dev/types/brand_retrieve_response.py +++ b/src/brand/dev/types/brand_retrieve_response.py @@ -470,6 +470,136 @@ class Brand(BaseModel): phone: Optional[str] = None """Company phone number""" + primary_language: Optional[ + Literal[ + "afrikaans", + "albanian", + "amharic", + "arabic", + "armenian", + "assamese", + "aymara", + "azeri", + "basque", + "belarusian", + "bengali", + "bosnian", + "bulgarian", + "burmese", + "cantonese", + "catalan", + "cebuano", + "chinese", + "corsican", + "croatian", + "czech", + "danish", + "dutch", + "english", + "esperanto", + "estonian", + "farsi", + "fijian", + "finnish", + "french", + "galician", + "georgian", + "german", + "greek", + "guarani", + "gujarati", + "haitian-creole", + "hausa", + "hawaiian", + "hebrew", + "hindi", + "hmong", + "hungarian", + "icelandic", + "igbo", + "indonesian", + "irish", + "italian", + "japanese", + "javanese", + "kannada", + "kazakh", + "khmer", + "kinyarwanda", + "korean", + "kurdish", + "kyrgyz", + "lao", + "latin", + "latvian", + "lingala", + "lithuanian", + "luxembourgish", + "macedonian", + "malagasy", + "malay", + "malayalam", + "maltese", + "maori", + "marathi", + "mongolian", + "nepali", + "norwegian", + "odia", + "oromo", + "pashto", + "pidgin", + "polish", + "portuguese", + "punjabi", + "quechua", + "romanian", + "russian", + "samoan", + "scottish-gaelic", + "serbian", + "sesotho", + "shona", + "sindhi", + "sinhala", + "slovak", + "slovene", + "somali", + "spanish", + "sundanese", + "swahili", + "swedish", + "tagalog", + "tajik", + "tamil", + "tatar", + "telugu", + "thai", + "tibetan", + "tigrinya", + "tongan", + "tswana", + "turkish", + "turkmen", + "ukrainian", + "urdu", + "uyghur", + "uzbek", + "vietnamese", + "welsh", + "wolof", + "xhosa", + "yiddish", + "yoruba", + "zulu", + ] + ] = None + """The primary language of the brand's website content. + + Detected from the HTML lang tag, page content analysis, or social media + descriptions. + """ + slogan: Optional[str] = None """The brand's slogan""" diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index 55875859..8f7b23ac 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -51,7 +51,7 @@ def test_method_retrieve(self, client: BrandDev) -> None: def test_method_retrieve_with_all_params(self, client: BrandDev) -> None: brand = client.brand.retrieve( domain="domain", - force_language="albanian", + force_language="afrikaans", max_speed=True, timeout_ms=1000, ) @@ -363,7 +363,7 @@ def test_method_identify_from_transaction_with_all_params(self, client: BrandDev transaction_info="transaction_info", city="city", country_gl="ad", - force_language="albanian", + force_language="afrikaans", high_confidence_only=True, max_speed=True, mcc="mcc", @@ -497,7 +497,7 @@ def test_method_retrieve_by_email(self, client: BrandDev) -> None: def test_method_retrieve_by_email_with_all_params(self, client: BrandDev) -> None: brand = client.brand.retrieve_by_email( email="dev@stainless.com", - force_language="albanian", + force_language="afrikaans", max_speed=True, timeout_ms=1000, ) @@ -542,7 +542,7 @@ def test_method_retrieve_by_isin(self, client: BrandDev) -> None: def test_method_retrieve_by_isin_with_all_params(self, client: BrandDev) -> None: brand = client.brand.retrieve_by_isin( isin="SE60513A9993", - force_language="albanian", + force_language="afrikaans", max_speed=True, timeout_ms=1000, ) @@ -588,7 +588,7 @@ def test_method_retrieve_by_name_with_all_params(self, client: BrandDev) -> None brand = client.brand.retrieve_by_name( name="xxx", country_gl="ad", - force_language="albanian", + force_language="afrikaans", max_speed=True, timeout_ms=1000, ) @@ -633,7 +633,7 @@ def test_method_retrieve_by_ticker(self, client: BrandDev) -> None: def test_method_retrieve_by_ticker_with_all_params(self, client: BrandDev) -> None: brand = client.brand.retrieve_by_ticker( ticker="ticker", - force_language="albanian", + force_language="afrikaans", max_speed=True, ticker_exchange="AMEX", timeout_ms=1000, @@ -1013,7 +1013,7 @@ async def test_method_retrieve(self, async_client: AsyncBrandDev) -> None: async def test_method_retrieve_with_all_params(self, async_client: AsyncBrandDev) -> None: brand = await async_client.brand.retrieve( domain="domain", - force_language="albanian", + force_language="afrikaans", max_speed=True, timeout_ms=1000, ) @@ -1325,7 +1325,7 @@ async def test_method_identify_from_transaction_with_all_params(self, async_clie transaction_info="transaction_info", city="city", country_gl="ad", - force_language="albanian", + force_language="afrikaans", high_confidence_only=True, max_speed=True, mcc="mcc", @@ -1459,7 +1459,7 @@ async def test_method_retrieve_by_email(self, async_client: AsyncBrandDev) -> No async def test_method_retrieve_by_email_with_all_params(self, async_client: AsyncBrandDev) -> None: brand = await async_client.brand.retrieve_by_email( email="dev@stainless.com", - force_language="albanian", + force_language="afrikaans", max_speed=True, timeout_ms=1000, ) @@ -1504,7 +1504,7 @@ async def test_method_retrieve_by_isin(self, async_client: AsyncBrandDev) -> Non async def test_method_retrieve_by_isin_with_all_params(self, async_client: AsyncBrandDev) -> None: brand = await async_client.brand.retrieve_by_isin( isin="SE60513A9993", - force_language="albanian", + force_language="afrikaans", max_speed=True, timeout_ms=1000, ) @@ -1550,7 +1550,7 @@ async def test_method_retrieve_by_name_with_all_params(self, async_client: Async brand = await async_client.brand.retrieve_by_name( name="xxx", country_gl="ad", - force_language="albanian", + force_language="afrikaans", max_speed=True, timeout_ms=1000, ) @@ -1595,7 +1595,7 @@ async def test_method_retrieve_by_ticker(self, async_client: AsyncBrandDev) -> N async def test_method_retrieve_by_ticker_with_all_params(self, async_client: AsyncBrandDev) -> None: brand = await async_client.brand.retrieve_by_ticker( ticker="ticker", - force_language="albanian", + force_language="afrikaans", max_speed=True, ticker_exchange="AMEX", timeout_ms=1000, From a388d39c93b50ec075a553957081a45f64b73d39 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 5 Apr 2026 15:20:40 +0000 Subject: [PATCH 03/82] feat(api): api update --- .stats.yml | 4 +- ...rand_identify_from_transaction_response.py | 38 ++++++++++++++++++- .../types/brand_retrieve_by_email_response.py | 38 ++++++++++++++++++- .../types/brand_retrieve_by_isin_response.py | 38 ++++++++++++++++++- .../types/brand_retrieve_by_name_response.py | 38 ++++++++++++++++++- .../brand_retrieve_by_ticker_response.py | 38 ++++++++++++++++++- .../dev/types/brand_retrieve_response.py | 38 ++++++++++++++++++- 7 files changed, 218 insertions(+), 14 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7c16337f..eb616189 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-095758df21a0bad753cde157cc0216339f77aec5b01de9bf36d55e34770cb611.yml -openapi_spec_hash: 4d5456700d25c12524ca030bb93d4261 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-f2509d0b758e88572e5a8145b8296cb330154727d9797e615ad19e44e050af26.yml +openapi_spec_hash: 40640a033ee09bda79ec066ba3744206 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/types/brand_identify_from_transaction_response.py b/src/brand/dev/types/brand_identify_from_transaction_response.py index 866c25f4..5d499eaf 100644 --- a/src/brand/dev/types/brand_identify_from_transaction_response.py +++ b/src/brand/dev/types/brand_identify_from_transaction_response.py @@ -415,8 +415,42 @@ class BrandLogo(BaseModel): class BrandSocial(BaseModel): - type: Optional[str] = None - """Type of social media, e.g., 'facebook', 'twitter'""" + type: Optional[ + Literal[ + "x", + "facebook", + "instagram", + "linkedin", + "youtube", + "pinterest", + "tiktok", + "dribbble", + "github", + "behance", + "snapchat", + "whatsapp", + "telegram", + "line", + "discord", + "twitch", + "vimeo", + "imdb", + "tumblr", + "flickr", + "giphy", + "medium", + "spotify", + "soundcloud", + "tripadvisor", + "yelp", + "producthunt", + "reddit", + "crunchbase", + "appstore", + "playstore", + ] + ] = None + """Type of social media platform""" url: Optional[str] = None """URL of the social media page""" diff --git a/src/brand/dev/types/brand_retrieve_by_email_response.py b/src/brand/dev/types/brand_retrieve_by_email_response.py index f7bb8e18..db6b8620 100644 --- a/src/brand/dev/types/brand_retrieve_by_email_response.py +++ b/src/brand/dev/types/brand_retrieve_by_email_response.py @@ -415,8 +415,42 @@ class BrandLogo(BaseModel): class BrandSocial(BaseModel): - type: Optional[str] = None - """Type of social media, e.g., 'facebook', 'twitter'""" + type: Optional[ + Literal[ + "x", + "facebook", + "instagram", + "linkedin", + "youtube", + "pinterest", + "tiktok", + "dribbble", + "github", + "behance", + "snapchat", + "whatsapp", + "telegram", + "line", + "discord", + "twitch", + "vimeo", + "imdb", + "tumblr", + "flickr", + "giphy", + "medium", + "spotify", + "soundcloud", + "tripadvisor", + "yelp", + "producthunt", + "reddit", + "crunchbase", + "appstore", + "playstore", + ] + ] = None + """Type of social media platform""" url: Optional[str] = None """URL of the social media page""" diff --git a/src/brand/dev/types/brand_retrieve_by_isin_response.py b/src/brand/dev/types/brand_retrieve_by_isin_response.py index 4e15e34f..3d080e9d 100644 --- a/src/brand/dev/types/brand_retrieve_by_isin_response.py +++ b/src/brand/dev/types/brand_retrieve_by_isin_response.py @@ -415,8 +415,42 @@ class BrandLogo(BaseModel): class BrandSocial(BaseModel): - type: Optional[str] = None - """Type of social media, e.g., 'facebook', 'twitter'""" + type: Optional[ + Literal[ + "x", + "facebook", + "instagram", + "linkedin", + "youtube", + "pinterest", + "tiktok", + "dribbble", + "github", + "behance", + "snapchat", + "whatsapp", + "telegram", + "line", + "discord", + "twitch", + "vimeo", + "imdb", + "tumblr", + "flickr", + "giphy", + "medium", + "spotify", + "soundcloud", + "tripadvisor", + "yelp", + "producthunt", + "reddit", + "crunchbase", + "appstore", + "playstore", + ] + ] = None + """Type of social media platform""" url: Optional[str] = None """URL of the social media page""" diff --git a/src/brand/dev/types/brand_retrieve_by_name_response.py b/src/brand/dev/types/brand_retrieve_by_name_response.py index cc242097..9a5b18e5 100644 --- a/src/brand/dev/types/brand_retrieve_by_name_response.py +++ b/src/brand/dev/types/brand_retrieve_by_name_response.py @@ -415,8 +415,42 @@ class BrandLogo(BaseModel): class BrandSocial(BaseModel): - type: Optional[str] = None - """Type of social media, e.g., 'facebook', 'twitter'""" + type: Optional[ + Literal[ + "x", + "facebook", + "instagram", + "linkedin", + "youtube", + "pinterest", + "tiktok", + "dribbble", + "github", + "behance", + "snapchat", + "whatsapp", + "telegram", + "line", + "discord", + "twitch", + "vimeo", + "imdb", + "tumblr", + "flickr", + "giphy", + "medium", + "spotify", + "soundcloud", + "tripadvisor", + "yelp", + "producthunt", + "reddit", + "crunchbase", + "appstore", + "playstore", + ] + ] = None + """Type of social media platform""" url: Optional[str] = None """URL of the social media page""" diff --git a/src/brand/dev/types/brand_retrieve_by_ticker_response.py b/src/brand/dev/types/brand_retrieve_by_ticker_response.py index a22f66f9..964d7a45 100644 --- a/src/brand/dev/types/brand_retrieve_by_ticker_response.py +++ b/src/brand/dev/types/brand_retrieve_by_ticker_response.py @@ -415,8 +415,42 @@ class BrandLogo(BaseModel): class BrandSocial(BaseModel): - type: Optional[str] = None - """Type of social media, e.g., 'facebook', 'twitter'""" + type: Optional[ + Literal[ + "x", + "facebook", + "instagram", + "linkedin", + "youtube", + "pinterest", + "tiktok", + "dribbble", + "github", + "behance", + "snapchat", + "whatsapp", + "telegram", + "line", + "discord", + "twitch", + "vimeo", + "imdb", + "tumblr", + "flickr", + "giphy", + "medium", + "spotify", + "soundcloud", + "tripadvisor", + "yelp", + "producthunt", + "reddit", + "crunchbase", + "appstore", + "playstore", + ] + ] = None + """Type of social media platform""" url: Optional[str] = None """URL of the social media page""" diff --git a/src/brand/dev/types/brand_retrieve_response.py b/src/brand/dev/types/brand_retrieve_response.py index 207c631b..d272dab6 100644 --- a/src/brand/dev/types/brand_retrieve_response.py +++ b/src/brand/dev/types/brand_retrieve_response.py @@ -415,8 +415,42 @@ class BrandLogo(BaseModel): class BrandSocial(BaseModel): - type: Optional[str] = None - """Type of social media, e.g., 'facebook', 'twitter'""" + type: Optional[ + Literal[ + "x", + "facebook", + "instagram", + "linkedin", + "youtube", + "pinterest", + "tiktok", + "dribbble", + "github", + "behance", + "snapchat", + "whatsapp", + "telegram", + "line", + "discord", + "twitch", + "vimeo", + "imdb", + "tumblr", + "flickr", + "giphy", + "medium", + "spotify", + "soundcloud", + "tripadvisor", + "yelp", + "producthunt", + "reddit", + "crunchbase", + "appstore", + "playstore", + ] + ] = None + """Type of social media platform""" url: Optional[str] = None """URL of the social media page""" From a92d7b7c66f52b259cbf31d938c8a70565190b5c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 04:21:59 +0000 Subject: [PATCH 04/82] fix(client): preserve hardcoded query params when merging with user params --- src/brand/dev/_base_client.py | 4 +++ tests/test_client.py | 48 +++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/brand/dev/_base_client.py b/src/brand/dev/_base_client.py index dc4af6b1..ce73b2f5 100644 --- a/src/brand/dev/_base_client.py +++ b/src/brand/dev/_base_client.py @@ -540,6 +540,10 @@ def _build_request( files = cast(HttpxRequestFiles, ForceMultipartDict()) prepared_url = self._prepare_url(options.url) + # preserve hard-coded query params from the url + if params and prepared_url.query: + params = {**dict(prepared_url.params.items()), **params} + prepared_url = prepared_url.copy_with(raw_path=prepared_url.raw_path.split(b"?", 1)[0]) if "_" in prepared_url.host: # work around https://github.com/encode/httpx/discussions/2880 kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} diff --git a/tests/test_client.py b/tests/test_client.py index 9a1bb3de..56223938 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -429,6 +429,30 @@ def test_default_query_option(self) -> None: client.close() + def test_hardcoded_query_params_in_url(self, client: BrandDev) -> None: + request = client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/foo?beta=true", + params={"limit": "10", "page": "abc"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/files/a%2Fb?beta=true", + params={"limit": "10"}, + ) + ) + assert request.url.raw_path == b"/files/a%2Fb?beta=true&limit=10" + def test_request_extra_json(self, client: BrandDev) -> None: request = client._build_request( FinalRequestOptions( @@ -1324,6 +1348,30 @@ async def test_default_query_option(self) -> None: await client.close() + async def test_hardcoded_query_params_in_url(self, async_client: AsyncBrandDev) -> None: + request = async_client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true"} + + request = async_client._build_request( + FinalRequestOptions( + method="get", + url="/foo?beta=true", + params={"limit": "10", "page": "abc"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} + + request = async_client._build_request( + FinalRequestOptions( + method="get", + url="/files/a%2Fb?beta=true", + params={"limit": "10"}, + ) + ) + assert request.url.raw_path == b"/files/a%2Fb?beta=true&limit=10" + def test_request_extra_json(self, client: BrandDev) -> None: request = client._build_request( FinalRequestOptions( From bd0f93f4df85dc1e822ca6b59fb7ce594976ad49 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 15:46:22 +0000 Subject: [PATCH 05/82] feat(api): api update --- .stats.yml | 4 +-- src/brand/dev/resources/brand.py | 32 ++++++++++++++----- .../dev/types/brand_screenshot_params.py | 14 ++++++-- tests/api_resources/test_brand.py | 26 +++++---------- 4 files changed, 45 insertions(+), 31 deletions(-) diff --git a/.stats.yml b/.stats.yml index eb616189..97c1cfd7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-f2509d0b758e88572e5a8145b8296cb330154727d9797e615ad19e44e050af26.yml -openapi_spec_hash: 40640a033ee09bda79ec066ba3744206 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-905e35387d755a84a2e7cd711f31eeea130e08a83bb7b39d3fa5445a187ef62f.yml +openapi_spec_hash: 205b2c17cb40ad54d76c2c06c9e5dda4 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index bda4613f..b2b9bc3f 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -2244,7 +2244,8 @@ def retrieve_simplified( def screenshot( self, *, - domain: str, + direct_url: str | Omit = omit, + domain: str | Omit = omit, full_screenshot: Literal["true", "false"] | Omit = omit, page: Literal["login", "signup", "blog", "careers", "pricing", "terms", "privacy", "contact"] | Omit = omit, prioritize: Literal["speed", "quality"] | Omit = omit, @@ -2259,10 +2260,15 @@ def screenshot( Supports both viewport (standard browser view) and full-page screenshots. Can also screenshot specific page types (login, - pricing, etc.) by using heuristics to find the appropriate URL. Returns a URL to - the uploaded screenshot image hosted on our CDN. + pricing, etc.) by using heuristics to find the appropriate URL. Either 'domain' + or 'directUrl' must be provided as a query parameter, but not both. Returns a + URL to the uploaded screenshot image hosted on our CDN. Args: + direct_url: A specific URL to screenshot directly, bypassing domain resolution (e.g., + 'https://example.com/pricing'). When provided, the screenshot is taken of this + exact URL. + domain: Domain name to take screenshot of (e.g., 'example.com', 'google.com'). The domain will be automatically normalized and validated. @@ -2273,7 +2279,8 @@ def screenshot( page: Optional parameter to specify which page type to screenshot. If provided, the system will scrape the domain's links and use heuristics to find the most appropriate URL for the specified page type (30 supported languages). If not - provided, screenshots the main domain landing page. + provided, screenshots the main domain landing page. Only applicable when using + 'domain', not 'directUrl'. prioritize: Optional parameter to prioritize screenshot capture. If 'speed', optimizes for faster capture with basic quality. If 'quality', optimizes for higher quality @@ -2296,6 +2303,7 @@ def screenshot( timeout=timeout, query=maybe_transform( { + "direct_url": direct_url, "domain": domain, "full_screenshot": full_screenshot, "page": page, @@ -4733,7 +4741,8 @@ async def retrieve_simplified( async def screenshot( self, *, - domain: str, + direct_url: str | Omit = omit, + domain: str | Omit = omit, full_screenshot: Literal["true", "false"] | Omit = omit, page: Literal["login", "signup", "blog", "careers", "pricing", "terms", "privacy", "contact"] | Omit = omit, prioritize: Literal["speed", "quality"] | Omit = omit, @@ -4748,10 +4757,15 @@ async def screenshot( Supports both viewport (standard browser view) and full-page screenshots. Can also screenshot specific page types (login, - pricing, etc.) by using heuristics to find the appropriate URL. Returns a URL to - the uploaded screenshot image hosted on our CDN. + pricing, etc.) by using heuristics to find the appropriate URL. Either 'domain' + or 'directUrl' must be provided as a query parameter, but not both. Returns a + URL to the uploaded screenshot image hosted on our CDN. Args: + direct_url: A specific URL to screenshot directly, bypassing domain resolution (e.g., + 'https://example.com/pricing'). When provided, the screenshot is taken of this + exact URL. + domain: Domain name to take screenshot of (e.g., 'example.com', 'google.com'). The domain will be automatically normalized and validated. @@ -4762,7 +4776,8 @@ async def screenshot( page: Optional parameter to specify which page type to screenshot. If provided, the system will scrape the domain's links and use heuristics to find the most appropriate URL for the specified page type (30 supported languages). If not - provided, screenshots the main domain landing page. + provided, screenshots the main domain landing page. Only applicable when using + 'domain', not 'directUrl'. prioritize: Optional parameter to prioritize screenshot capture. If 'speed', optimizes for faster capture with basic quality. If 'quality', optimizes for higher quality @@ -4785,6 +4800,7 @@ async def screenshot( timeout=timeout, query=await async_maybe_transform( { + "direct_url": direct_url, "domain": domain, "full_screenshot": full_screenshot, "page": page, diff --git a/src/brand/dev/types/brand_screenshot_params.py b/src/brand/dev/types/brand_screenshot_params.py index 4f26b1fb..b71ac5ca 100644 --- a/src/brand/dev/types/brand_screenshot_params.py +++ b/src/brand/dev/types/brand_screenshot_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Literal, Required, Annotated, TypedDict +from typing_extensions import Literal, Annotated, TypedDict from .._utils import PropertyInfo @@ -10,7 +10,14 @@ class BrandScreenshotParams(TypedDict, total=False): - domain: Required[str] + direct_url: Annotated[str, PropertyInfo(alias="directUrl")] + """ + A specific URL to screenshot directly, bypassing domain resolution (e.g., + 'https://example.com/pricing'). When provided, the screenshot is taken of this + exact URL. + """ + + domain: str """Domain name to take screenshot of (e.g., 'example.com', 'google.com'). The domain will be automatically normalized and validated. @@ -28,7 +35,8 @@ class BrandScreenshotParams(TypedDict, total=False): If provided, the system will scrape the domain's links and use heuristics to find the most appropriate URL for the specified page type (30 supported - languages). If not provided, screenshots the main domain landing page. + languages). If not provided, screenshots the main domain landing page. Only + applicable when using 'domain', not 'directUrl'. """ prioritize: Literal["speed", "quality"] diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index 8f7b23ac..f16aa0a4 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -757,15 +757,14 @@ def test_streaming_response_retrieve_simplified(self, client: BrandDev) -> None: @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_screenshot(self, client: BrandDev) -> None: - brand = client.brand.screenshot( - domain="domain", - ) + brand = client.brand.screenshot() assert_matches_type(BrandScreenshotResponse, brand, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_screenshot_with_all_params(self, client: BrandDev) -> None: brand = client.brand.screenshot( + direct_url="https://example.com", domain="domain", full_screenshot="true", page="login", @@ -776,9 +775,7 @@ def test_method_screenshot_with_all_params(self, client: BrandDev) -> None: @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_raw_response_screenshot(self, client: BrandDev) -> None: - response = client.brand.with_raw_response.screenshot( - domain="domain", - ) + response = client.brand.with_raw_response.screenshot() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -788,9 +785,7 @@ def test_raw_response_screenshot(self, client: BrandDev) -> None: @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_streaming_response_screenshot(self, client: BrandDev) -> None: - with client.brand.with_streaming_response.screenshot( - domain="domain", - ) as response: + with client.brand.with_streaming_response.screenshot() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -1719,15 +1714,14 @@ async def test_streaming_response_retrieve_simplified(self, async_client: AsyncB @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_method_screenshot(self, async_client: AsyncBrandDev) -> None: - brand = await async_client.brand.screenshot( - domain="domain", - ) + brand = await async_client.brand.screenshot() assert_matches_type(BrandScreenshotResponse, brand, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_method_screenshot_with_all_params(self, async_client: AsyncBrandDev) -> None: brand = await async_client.brand.screenshot( + direct_url="https://example.com", domain="domain", full_screenshot="true", page="login", @@ -1738,9 +1732,7 @@ async def test_method_screenshot_with_all_params(self, async_client: AsyncBrandD @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_raw_response_screenshot(self, async_client: AsyncBrandDev) -> None: - response = await async_client.brand.with_raw_response.screenshot( - domain="domain", - ) + response = await async_client.brand.with_raw_response.screenshot() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -1750,9 +1742,7 @@ async def test_raw_response_screenshot(self, async_client: AsyncBrandDev) -> Non @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_streaming_response_screenshot(self, async_client: AsyncBrandDev) -> None: - async with async_client.brand.with_streaming_response.screenshot( - domain="domain", - ) as response: + async with async_client.brand.with_streaming_response.screenshot() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" From 9b72a51afe0d9992f3c52ee727c1ffc1ec76312f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 11 Apr 2026 07:15:49 +0000 Subject: [PATCH 06/82] fix: ensure file data are only sent as 1 parameter --- src/brand/dev/_utils/_utils.py | 5 +++-- tests/test_extract_files.py | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/brand/dev/_utils/_utils.py b/src/brand/dev/_utils/_utils.py index eec7f4a1..63b8cd60 100644 --- a/src/brand/dev/_utils/_utils.py +++ b/src/brand/dev/_utils/_utils.py @@ -86,8 +86,9 @@ def _extract_items( index += 1 if is_dict(obj): try: - # We are at the last entry in the path so we must remove the field - if (len(path)) == index: + # Remove the field if there are no more dict keys in the path, + # only "" traversal markers or end. + if all(p == "" for p in path[index:]): item = obj.pop(key) else: item = obj[key] diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py index 72d0a8b6..af7ecd37 100644 --- a/tests/test_extract_files.py +++ b/tests/test_extract_files.py @@ -35,6 +35,15 @@ def test_multiple_files() -> None: assert query == {"documents": [{}, {}]} +def test_top_level_file_array() -> None: + query = {"files": [b"file one", b"file two"], "title": "hello"} + assert extract_files(query, paths=[["files", ""]]) == [ + ("files[]", b"file one"), + ("files[]", b"file two"), + ] + assert query == {"title": "hello"} + + @pytest.mark.parametrize( "query,paths,expected", [ From 7d2dd3b283bf81c28fd78ac641c6a4bc7cceab79 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 12 Apr 2026 14:44:24 +0000 Subject: [PATCH 07/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 97c1cfd7..f82697e3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-905e35387d755a84a2e7cd711f31eeea130e08a83bb7b39d3fa5445a187ef62f.yml -openapi_spec_hash: 205b2c17cb40ad54d76c2c06c9e5dda4 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-40b1d672fe922d8157775c48539332de9a8bff757a174db394ec5f1e3968678b.yml +openapi_spec_hash: ae60ee9eafb3bcb77ba255818e7d228d config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 721f3b47714c67349bbd01d92c9b763c07b527fb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 12 Apr 2026 14:53:20 +0000 Subject: [PATCH 08/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index f82697e3..4f2e69a4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-40b1d672fe922d8157775c48539332de9a8bff757a174db394ec5f1e3968678b.yml -openapi_spec_hash: ae60ee9eafb3bcb77ba255818e7d228d +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-0b41a16de26363ad6880dc82a92a24f4c44b5cd8435344720383619d6019e792.yml +openapi_spec_hash: fc92550089eb09bd802e4178bbde83c0 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 223fdb8ae1c8db17e4efca1e705d8105d1289d7e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 12 Apr 2026 14:58:26 +0000 Subject: [PATCH 09/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 4f2e69a4..b49f2b4a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-0b41a16de26363ad6880dc82a92a24f4c44b5cd8435344720383619d6019e792.yml -openapi_spec_hash: fc92550089eb09bd802e4178bbde83c0 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-57f28dde4f89132b3ad99235eb8940391a3f6756c2fd0eafe4e2a0615aa32d50.yml +openapi_spec_hash: d7771a48eb4480cb0868d3af901fe360 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 4077db397125115d11aba161d31c13a88beb9728 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 12 Apr 2026 21:16:24 +0000 Subject: [PATCH 10/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index b49f2b4a..2eee2eae 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-57f28dde4f89132b3ad99235eb8940391a3f6756c2fd0eafe4e2a0615aa32d50.yml -openapi_spec_hash: d7771a48eb4480cb0868d3af901fe360 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-4876a8799d99d7b497a352f6b873e53cff8b98019d4070e65fd272eed87ccda4.yml +openapi_spec_hash: c095133c2695510842b84cf5820a16a4 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 04fafa1b94a4047cc9fb3825ab84e6f517baefc4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 14:43:10 +0000 Subject: [PATCH 11/82] feat(api): api update --- .stats.yml | 4 +-- .../dev/types/brand_styleguide_response.py | 34 ++++++++++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 2eee2eae..9b7576d8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-4876a8799d99d7b497a352f6b873e53cff8b98019d4070e65fd272eed87ccda4.yml -openapi_spec_hash: c095133c2695510842b84cf5820a16a4 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-e7d0d9b555ca50f58e651f918238ee1329755f46cb962feaedec097be4c28d83.yml +openapi_spec_hash: b187dde21c352dd66cf249fbc14dd0b4 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/types/brand_styleguide_response.py b/src/brand/dev/types/brand_styleguide_response.py index edc2d296..95102038 100644 --- a/src/brand/dev/types/brand_styleguide_response.py +++ b/src/brand/dev/types/brand_styleguide_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional +from typing import Dict, List, Optional from typing_extensions import Literal from pydantic import Field as FieldInfo @@ -18,6 +18,7 @@ "StyleguideComponentsButtonSecondary", "StyleguideComponentsCard", "StyleguideElementSpacing", + "StyleguideFontLinks", "StyleguideShadows", "StyleguideTypography", "StyleguideTypographyHeadings", @@ -244,6 +245,30 @@ class StyleguideElementSpacing(BaseModel): xs: str +class StyleguideFontLinks(BaseModel): + files: Dict[str, str] + """Upright font files keyed by weight string (e.g. + + "400" for regular, "500", "700"). Values are absolute URLs. + """ + + type: Literal["google", "custom"] + + category: Optional[str] = None + """Google Fonts category when type is google (e.g. + + sans-serif, serif, monospace, display, handwriting). Omitted for custom fonts + when unknown. + """ + + display_name: Optional[str] = FieldInfo(alias="displayName", default=None) + """ + Present when type is custom: human-readable name derived from the fontLinks key + (strip build/hash suffixes, split camelCase / PascalCase, normalize separators). + Google entries omit this. + """ + + class StyleguideShadows(BaseModel): """Shadow styles used on the website""" @@ -371,6 +396,13 @@ class Styleguide(BaseModel): element_spacing: StyleguideElementSpacing = FieldInfo(alias="elementSpacing") """Spacing system used on the website""" + font_links: Dict[str, StyleguideFontLinks] = FieldInfo(alias="fontLinks") + """ + Font assets keyed by family name as it appears in fontFamily/fontFallbacks + (non-generic names only). Clients match typography.fontFamily / fontWeight or + button styles to pick a file URL from files. + """ + mode: Literal["light", "dark"] """The primary color mode of the website design""" From b5e8905b3e8554890bda8c7eb9e4255c363f1a81 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 01:19:27 +0000 Subject: [PATCH 12/82] feat(api): api update --- .stats.yml | 4 +- src/brand/dev/resources/brand.py | 38 ++++++++++++++++++- .../dev/types/brand_web_scrape_html_params.py | 11 +++++- .../dev/types/brand_web_scrape_md_params.py | 7 ++++ tests/api_resources/test_brand.py | 20 ++++++++++ 5 files changed, 75 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index 9b7576d8..41aba4a2 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-e7d0d9b555ca50f58e651f918238ee1329755f46cb962feaedec097be4c28d83.yml -openapi_spec_hash: b187dde21c352dd66cf249fbc14dd0b4 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-4bc00d073aa6c38b3299c9fc2e197ba9e25df5defca2508a0a0c83bf08237b00.yml +openapi_spec_hash: b68c73667402fd727825555413522ce6 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index b2b9bc3f..70141837 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -2376,6 +2376,7 @@ def web_scrape_html( self, *, url: str, + max_age_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2389,6 +2390,10 @@ def web_scrape_html( Args: url: Full URL to scrape (must include http:// or https:// protocol) + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 1 day (86400000 ms) when + omitted. Set to 0 to always scrape fresh. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2404,7 +2409,13 @@ def web_scrape_html( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform({"url": url}, brand_web_scrape_html_params.BrandWebScrapeHTMLParams), + query=maybe_transform( + { + "url": url, + "max_age_ms": max_age_ms, + }, + brand_web_scrape_html_params.BrandWebScrapeHTMLParams, + ), ), cast_to=BrandWebScrapeHTMLResponse, ) @@ -2455,6 +2466,7 @@ def web_scrape_md( url: str, include_images: bool | Omit = omit, include_links: bool | Omit = omit, + max_age_ms: int | Omit = omit, shorten_base64_images: bool | Omit = omit, use_main_content_only: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -2476,6 +2488,10 @@ def web_scrape_md( include_links: Preserve hyperlinks in Markdown output + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 1 day (86400000 ms) when + omitted. Set to 0 to always scrape fresh. + shorten_base64_images: Shorten base64-encoded image data in the Markdown output use_main_content_only: Extract only the main content of the page, excluding headers, footers, sidebars, @@ -2501,6 +2517,7 @@ def web_scrape_md( "url": url, "include_images": include_images, "include_links": include_links, + "max_age_ms": max_age_ms, "shorten_base64_images": shorten_base64_images, "use_main_content_only": use_main_content_only, }, @@ -4873,6 +4890,7 @@ async def web_scrape_html( self, *, url: str, + max_age_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -4886,6 +4904,10 @@ async def web_scrape_html( Args: url: Full URL to scrape (must include http:// or https:// protocol) + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 1 day (86400000 ms) when + omitted. Set to 0 to always scrape fresh. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -4901,7 +4923,13 @@ async def web_scrape_html( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=await async_maybe_transform({"url": url}, brand_web_scrape_html_params.BrandWebScrapeHTMLParams), + query=await async_maybe_transform( + { + "url": url, + "max_age_ms": max_age_ms, + }, + brand_web_scrape_html_params.BrandWebScrapeHTMLParams, + ), ), cast_to=BrandWebScrapeHTMLResponse, ) @@ -4954,6 +4982,7 @@ async def web_scrape_md( url: str, include_images: bool | Omit = omit, include_links: bool | Omit = omit, + max_age_ms: int | Omit = omit, shorten_base64_images: bool | Omit = omit, use_main_content_only: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -4975,6 +5004,10 @@ async def web_scrape_md( include_links: Preserve hyperlinks in Markdown output + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 1 day (86400000 ms) when + omitted. Set to 0 to always scrape fresh. + shorten_base64_images: Shorten base64-encoded image data in the Markdown output use_main_content_only: Extract only the main content of the page, excluding headers, footers, sidebars, @@ -5000,6 +5033,7 @@ async def web_scrape_md( "url": url, "include_images": include_images, "include_links": include_links, + "max_age_ms": max_age_ms, "shorten_base64_images": shorten_base64_images, "use_main_content_only": use_main_content_only, }, diff --git a/src/brand/dev/types/brand_web_scrape_html_params.py b/src/brand/dev/types/brand_web_scrape_html_params.py index 86246f42..a65f4d87 100644 --- a/src/brand/dev/types/brand_web_scrape_html_params.py +++ b/src/brand/dev/types/brand_web_scrape_html_params.py @@ -2,7 +2,9 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo __all__ = ["BrandWebScrapeHTMLParams"] @@ -10,3 +12,10 @@ class BrandWebScrapeHTMLParams(TypedDict, total=False): url: Required[str] """Full URL to scrape (must include http:// or https:// protocol)""" + + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] + """ + Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 1 day (86400000 ms) when + omitted. Set to 0 to always scrape fresh. + """ diff --git a/src/brand/dev/types/brand_web_scrape_md_params.py b/src/brand/dev/types/brand_web_scrape_md_params.py index 6bf905eb..6ab58339 100644 --- a/src/brand/dev/types/brand_web_scrape_md_params.py +++ b/src/brand/dev/types/brand_web_scrape_md_params.py @@ -22,6 +22,13 @@ class BrandWebScrapeMdParams(TypedDict, total=False): include_links: Annotated[bool, PropertyInfo(alias="includeLinks")] """Preserve hyperlinks in Markdown output""" + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] + """ + Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 1 day (86400000 ms) when + omitted. Set to 0 to always scrape fresh. + """ + shorten_base64_images: Annotated[bool, PropertyInfo(alias="shortenBase64Images")] """Shorten base64-encoded image data in the Markdown output""" diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index f16aa0a4..6dae4f80 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -840,6 +840,15 @@ def test_method_web_scrape_html(self, client: BrandDev) -> None: ) assert_matches_type(BrandWebScrapeHTMLResponse, brand, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_web_scrape_html_with_all_params(self, client: BrandDev) -> None: + brand = client.brand.web_scrape_html( + url="https://example.com", + max_age_ms=0, + ) + assert_matches_type(BrandWebScrapeHTMLResponse, brand, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_raw_response_web_scrape_html(self, client: BrandDev) -> None: @@ -915,6 +924,7 @@ def test_method_web_scrape_md_with_all_params(self, client: BrandDev) -> None: url="https://example.com", include_images=True, include_links=True, + max_age_ms=0, shorten_base64_images=True, use_main_content_only=True, ) @@ -1797,6 +1807,15 @@ async def test_method_web_scrape_html(self, async_client: AsyncBrandDev) -> None ) assert_matches_type(BrandWebScrapeHTMLResponse, brand, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_web_scrape_html_with_all_params(self, async_client: AsyncBrandDev) -> None: + brand = await async_client.brand.web_scrape_html( + url="https://example.com", + max_age_ms=0, + ) + assert_matches_type(BrandWebScrapeHTMLResponse, brand, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_raw_response_web_scrape_html(self, async_client: AsyncBrandDev) -> None: @@ -1872,6 +1891,7 @@ async def test_method_web_scrape_md_with_all_params(self, async_client: AsyncBra url="https://example.com", include_images=True, include_links=True, + max_age_ms=0, shorten_base64_images=True, use_main_content_only=True, ) From bf48ceb8693e734ab3da2b89e80f394d351215e6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 01:36:07 +0000 Subject: [PATCH 13/82] feat(api): api update --- .stats.yml | 4 ++-- src/brand/dev/resources/brand.py | 4 ++-- src/brand/dev/types/brand_web_scrape_html_params.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index 41aba4a2..4caa3eb3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-4bc00d073aa6c38b3299c9fc2e197ba9e25df5defca2508a0a0c83bf08237b00.yml -openapi_spec_hash: b68c73667402fd727825555413522ce6 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-f15e63fe284d980e63fbbba5b3f845f91565f43e4c0b3182bb5d8bd55fda2a51.yml +openapi_spec_hash: 5f833c91cc4541722b10451a4629c1bc config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index 70141837..3df9b664 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -2392,7 +2392,7 @@ def web_scrape_html( max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when - omitted. Set to 0 to always scrape fresh. + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. extra_headers: Send extra headers @@ -4906,7 +4906,7 @@ async def web_scrape_html( max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when - omitted. Set to 0 to always scrape fresh. + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. extra_headers: Send extra headers diff --git a/src/brand/dev/types/brand_web_scrape_html_params.py b/src/brand/dev/types/brand_web_scrape_html_params.py index a65f4d87..ea7da75d 100644 --- a/src/brand/dev/types/brand_web_scrape_html_params.py +++ b/src/brand/dev/types/brand_web_scrape_html_params.py @@ -17,5 +17,5 @@ class BrandWebScrapeHTMLParams(TypedDict, total=False): """ Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when - omitted. Set to 0 to always scrape fresh. + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. """ From 93a9472c495a1d34afa82ff7a6889e32ff706e3f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 01:44:21 +0000 Subject: [PATCH 14/82] feat(api): api update --- .stats.yml | 4 ++-- src/brand/dev/resources/brand.py | 14 ++++++-------- src/brand/dev/types/brand_web_scrape_md_params.py | 4 ++-- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/.stats.yml b/.stats.yml index 4caa3eb3..792fcdf1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-f15e63fe284d980e63fbbba5b3f845f91565f43e4c0b3182bb5d8bd55fda2a51.yml -openapi_spec_hash: 5f833c91cc4541722b10451a4629c1bc +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-93cf699701172f25e4f6d5053b0573eeb51a79707de94a137ac23d0aaeb76e36.yml +openapi_spec_hash: 4abd14fccd70b0cb0397776e6b5b9171 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index 3df9b664..4b1f0c5c 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -2477,11 +2477,10 @@ def web_scrape_md( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandWebScrapeMdResponse: """ - Scrapes the given URL, converts the HTML content to Markdown, and returns the - result. + Scrapes the given URL into LLM usable Markdown. Args: - url: Full URL to scrape and convert to markdown (must include http:// or https:// + url: Full URL to scrape into LLM usable Markdown (must include http:// or https:// protocol) include_images: Include image references in Markdown output @@ -2490,7 +2489,7 @@ def web_scrape_md( max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when - omitted. Set to 0 to always scrape fresh. + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. shorten_base64_images: Shorten base64-encoded image data in the Markdown output @@ -4993,11 +4992,10 @@ async def web_scrape_md( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandWebScrapeMdResponse: """ - Scrapes the given URL, converts the HTML content to Markdown, and returns the - result. + Scrapes the given URL into LLM usable Markdown. Args: - url: Full URL to scrape and convert to markdown (must include http:// or https:// + url: Full URL to scrape into LLM usable Markdown (must include http:// or https:// protocol) include_images: Include image references in Markdown output @@ -5006,7 +5004,7 @@ async def web_scrape_md( max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when - omitted. Set to 0 to always scrape fresh. + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. shorten_base64_images: Shorten base64-encoded image data in the Markdown output diff --git a/src/brand/dev/types/brand_web_scrape_md_params.py b/src/brand/dev/types/brand_web_scrape_md_params.py index 6ab58339..e3964edd 100644 --- a/src/brand/dev/types/brand_web_scrape_md_params.py +++ b/src/brand/dev/types/brand_web_scrape_md_params.py @@ -12,7 +12,7 @@ class BrandWebScrapeMdParams(TypedDict, total=False): url: Required[str] """ - Full URL to scrape and convert to markdown (must include http:// or https:// + Full URL to scrape into LLM usable Markdown (must include http:// or https:// protocol) """ @@ -26,7 +26,7 @@ class BrandWebScrapeMdParams(TypedDict, total=False): """ Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when - omitted. Set to 0 to always scrape fresh. + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. """ shorten_base64_images: Annotated[bool, PropertyInfo(alias="shortenBase64Images")] From 1c9099a7002831ce62bb809a0d8f58ac755d2cd1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 01:55:48 +0000 Subject: [PATCH 15/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 792fcdf1..0e03d0e7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-93cf699701172f25e4f6d5053b0573eeb51a79707de94a137ac23d0aaeb76e36.yml -openapi_spec_hash: 4abd14fccd70b0cb0397776e6b5b9171 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-e1e6c71c933578dccc0c110054a5fa21347c4795dff14fd48f5873536b4ce959.yml +openapi_spec_hash: 12b5773c2b15c2f7cdce7590237a1125 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From f13cc2be3ce7c3355168ba62bb3bd5ec5aaf4105 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 02:06:58 +0000 Subject: [PATCH 16/82] feat(api): api update --- .stats.yml | 4 ++-- src/brand/dev/resources/brand.py | 14 ++++---------- .../dev/types/brand_web_scrape_sitemap_params.py | 5 +---- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0e03d0e7..ec6d2505 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-e1e6c71c933578dccc0c110054a5fa21347c4795dff14fd48f5873536b4ce959.yml -openapi_spec_hash: 12b5773c2b15c2f7cdce7590237a1125 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-2a7727122f4ea5f9240c4c701f70f91b48f861bf5d87ec64f6904d635be5aef8.yml +openapi_spec_hash: 51884daf875aba2f255aaaf69ef1c42d config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index 4b1f0c5c..ae40cbc7 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -2539,13 +2539,10 @@ def web_scrape_sitemap( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandWebScrapeSitemapResponse: """ - Crawls the sitemap of the given domain and returns all discovered page URLs. - Supports sitemap index files (recursive), parallel fetching with concurrency - control, deduplication, and filters out non-page resources (images, PDFs, etc.). + Crawl an entire website's sitemap and return all discovered page URLs Args: - domain: Domain name to crawl sitemaps for (e.g., 'example.com'). The domain will be - automatically normalized and validated. + domain: Domain to build a sitemap for max_links: Maximum number of links to return from the sitemap crawl. Defaults to 10,000. Minimum is 1, maximum is 100,000. @@ -5054,13 +5051,10 @@ async def web_scrape_sitemap( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandWebScrapeSitemapResponse: """ - Crawls the sitemap of the given domain and returns all discovered page URLs. - Supports sitemap index files (recursive), parallel fetching with concurrency - control, deduplication, and filters out non-page resources (images, PDFs, etc.). + Crawl an entire website's sitemap and return all discovered page URLs Args: - domain: Domain name to crawl sitemaps for (e.g., 'example.com'). The domain will be - automatically normalized and validated. + domain: Domain to build a sitemap for max_links: Maximum number of links to return from the sitemap crawl. Defaults to 10,000. Minimum is 1, maximum is 100,000. diff --git a/src/brand/dev/types/brand_web_scrape_sitemap_params.py b/src/brand/dev/types/brand_web_scrape_sitemap_params.py index e675dae3..8d556085 100644 --- a/src/brand/dev/types/brand_web_scrape_sitemap_params.py +++ b/src/brand/dev/types/brand_web_scrape_sitemap_params.py @@ -11,10 +11,7 @@ class BrandWebScrapeSitemapParams(TypedDict, total=False): domain: Required[str] - """Domain name to crawl sitemaps for (e.g., 'example.com'). - - The domain will be automatically normalized and validated. - """ + """Domain to build a sitemap for""" max_links: Annotated[int, PropertyInfo(alias="maxLinks")] """Maximum number of links to return from the sitemap crawl. From 23171da1332110970c1fdda5f92e49082ea59130 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 02:17:51 +0000 Subject: [PATCH 17/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index ec6d2505..ff47fd44 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-2a7727122f4ea5f9240c4c701f70f91b48f861bf5d87ec64f6904d635be5aef8.yml -openapi_spec_hash: 51884daf875aba2f255aaaf69ef1c42d +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-a3f9cb65527955359bd08122edd68527fb13d5ba8fa8279fb3892e277e1d6233.yml +openapi_spec_hash: 037b592f3b2373d9b0753ba5b7d571b5 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 2116fb95beafb7e9fe05e27955bf4ffed3151b16 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 02:24:44 +0000 Subject: [PATCH 18/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index ff47fd44..3e31f1c0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-a3f9cb65527955359bd08122edd68527fb13d5ba8fa8279fb3892e277e1d6233.yml -openapi_spec_hash: 037b592f3b2373d9b0753ba5b7d571b5 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-41fdbb36b66c713e8073c04937010aa4b861a99b105966856e211c9aaee1fe34.yml +openapi_spec_hash: daadd24dfc43587bb323a137089edd7d config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 9d4a64e28b1321eda8463d8c86ec8a130c78c318 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 21:55:54 +0000 Subject: [PATCH 19/82] feat(api): api update --- .stats.yml | 6 +- api.md | 2 - src/brand/dev/resources/brand.py | 114 -------------------- src/brand/dev/types/__init__.py | 2 - src/brand/dev/types/brand_fonts_params.py | 24 ----- src/brand/dev/types/brand_fonts_response.py | 44 -------- tests/api_resources/test_brand.py | 87 --------------- 7 files changed, 3 insertions(+), 276 deletions(-) delete mode 100644 src/brand/dev/types/brand_fonts_params.py delete mode 100644 src/brand/dev/types/brand_fonts_response.py diff --git a/.stats.yml b/.stats.yml index 3e31f1c0..c1176570 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-41fdbb36b66c713e8073c04937010aa4b861a99b105966856e211c9aaee1fe34.yml -openapi_spec_hash: daadd24dfc43587bb323a137089edd7d +configured_endpoints: 19 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-135cee98ff73868b77c6dd17f5d1ccc7cd1e26a8cef266ceba1020346e11a8ef.yml +openapi_spec_hash: d9c1751a0fd999118b6d55d4931212e1 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/api.md b/api.md index 8b5d705f..7a36584a 100644 --- a/api.md +++ b/api.md @@ -8,7 +8,6 @@ from brand.dev.types import ( BrandAIProductResponse, BrandAIProductsResponse, BrandAIQueryResponse, - BrandFontsResponse, BrandIdentifyFromTransactionResponse, BrandPrefetchResponse, BrandPrefetchByEmailResponse, @@ -33,7 +32,6 @@ Methods: - client.brand.ai_product(\*\*params) -> BrandAIProductResponse - client.brand.ai_products(\*\*params) -> BrandAIProductsResponse - client.brand.ai_query(\*\*params) -> BrandAIQueryResponse -- client.brand.fonts(\*\*params) -> BrandFontsResponse - client.brand.identify_from_transaction(\*\*params) -> BrandIdentifyFromTransactionResponse - client.brand.prefetch(\*\*params) -> BrandPrefetchResponse - client.brand.prefetch_by_email(\*\*params) -> BrandPrefetchByEmailResponse diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index ae40cbc7..d9b24627 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -8,7 +8,6 @@ import httpx from ..types import ( - brand_fonts_params, brand_ai_query_params, brand_prefetch_params, brand_retrieve_params, @@ -40,7 +39,6 @@ async_to_streamed_response_wrapper, ) from .._base_client import make_request_options -from ..types.brand_fonts_response import BrandFontsResponse from ..types.brand_ai_query_response import BrandAIQueryResponse from ..types.brand_prefetch_response import BrandPrefetchResponse from ..types.brand_retrieve_response import BrandRetrieveResponse @@ -477,56 +475,6 @@ def ai_query( cast_to=BrandAIQueryResponse, ) - def fonts( - self, - *, - domain: str, - timeout_ms: int | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BrandFontsResponse: - """ - Extract font information from a brand's website including font families, usage - statistics, fallbacks, and element/word counts. - - Args: - domain: Domain name to extract fonts from (e.g., 'example.com', 'google.com'). The - domain will be automatically normalized and validated. - - timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer - than this value, it will be aborted with a 408 status code. Maximum allowed - value is 300000ms (5 minutes). - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/brand/fonts", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "domain": domain, - "timeout_ms": timeout_ms, - }, - brand_fonts_params.BrandFontsParams, - ), - ), - cast_to=BrandFontsResponse, - ) - def identify_from_transaction( self, *, @@ -2987,56 +2935,6 @@ async def ai_query( cast_to=BrandAIQueryResponse, ) - async def fonts( - self, - *, - domain: str, - timeout_ms: int | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BrandFontsResponse: - """ - Extract font information from a brand's website including font families, usage - statistics, fallbacks, and element/word counts. - - Args: - domain: Domain name to extract fonts from (e.g., 'example.com', 'google.com'). The - domain will be automatically normalized and validated. - - timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer - than this value, it will be aborted with a 408 status code. Maximum allowed - value is 300000ms (5 minutes). - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/brand/fonts", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "domain": domain, - "timeout_ms": timeout_ms, - }, - brand_fonts_params.BrandFontsParams, - ), - ), - cast_to=BrandFontsResponse, - ) - async def identify_from_transaction( self, *, @@ -5102,9 +5000,6 @@ def __init__(self, brand: BrandResource) -> None: self.ai_query = to_raw_response_wrapper( brand.ai_query, ) - self.fonts = to_raw_response_wrapper( - brand.fonts, - ) self.identify_from_transaction = to_raw_response_wrapper( brand.identify_from_transaction, ) @@ -5168,9 +5063,6 @@ def __init__(self, brand: AsyncBrandResource) -> None: self.ai_query = async_to_raw_response_wrapper( brand.ai_query, ) - self.fonts = async_to_raw_response_wrapper( - brand.fonts, - ) self.identify_from_transaction = async_to_raw_response_wrapper( brand.identify_from_transaction, ) @@ -5234,9 +5126,6 @@ def __init__(self, brand: BrandResource) -> None: self.ai_query = to_streamed_response_wrapper( brand.ai_query, ) - self.fonts = to_streamed_response_wrapper( - brand.fonts, - ) self.identify_from_transaction = to_streamed_response_wrapper( brand.identify_from_transaction, ) @@ -5300,9 +5189,6 @@ def __init__(self, brand: AsyncBrandResource) -> None: self.ai_query = async_to_streamed_response_wrapper( brand.ai_query, ) - self.fonts = async_to_streamed_response_wrapper( - brand.fonts, - ) self.identify_from_transaction = async_to_streamed_response_wrapper( brand.identify_from_transaction, ) diff --git a/src/brand/dev/types/__init__.py b/src/brand/dev/types/__init__.py index 2b7129d9..5b665a91 100644 --- a/src/brand/dev/types/__init__.py +++ b/src/brand/dev/types/__init__.py @@ -2,8 +2,6 @@ from __future__ import annotations -from .brand_fonts_params import BrandFontsParams as BrandFontsParams -from .brand_fonts_response import BrandFontsResponse as BrandFontsResponse from .brand_ai_query_params import BrandAIQueryParams as BrandAIQueryParams from .brand_prefetch_params import BrandPrefetchParams as BrandPrefetchParams from .brand_retrieve_params import BrandRetrieveParams as BrandRetrieveParams diff --git a/src/brand/dev/types/brand_fonts_params.py b/src/brand/dev/types/brand_fonts_params.py deleted file mode 100644 index db13d2f5..00000000 --- a/src/brand/dev/types/brand_fonts_params.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["BrandFontsParams"] - - -class BrandFontsParams(TypedDict, total=False): - domain: Required[str] - """Domain name to extract fonts from (e.g., 'example.com', 'google.com'). - - The domain will be automatically normalized and validated. - """ - - timeout_ms: Annotated[int, PropertyInfo(alias="timeoutMS")] - """Optional timeout in milliseconds for the request. - - If the request takes longer than this value, it will be aborted with a 408 - status code. Maximum allowed value is 300000ms (5 minutes). - """ diff --git a/src/brand/dev/types/brand_fonts_response.py b/src/brand/dev/types/brand_fonts_response.py deleted file mode 100644 index 2721af9c..00000000 --- a/src/brand/dev/types/brand_fonts_response.py +++ /dev/null @@ -1,44 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List - -from .._models import BaseModel - -__all__ = ["BrandFontsResponse", "Font"] - - -class Font(BaseModel): - fallbacks: List[str] - """Array of fallback font families""" - - font: str - """Font family name""" - - num_elements: float - """Number of elements using this font""" - - num_words: float - """Number of words using this font""" - - percent_elements: float - """Percentage of elements using this font""" - - percent_words: float - """Percentage of words using this font""" - - uses: List[str] - """Array of CSS selectors or element types where this font is used""" - - -class BrandFontsResponse(BaseModel): - code: int - """HTTP status code, e.g., 200""" - - domain: str - """The normalized domain that was processed""" - - fonts: List[Font] - """Array of font usage information""" - - status: str - """Status of the response, e.g., 'ok'""" diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index 6dae4f80..7fd6aab0 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -10,7 +10,6 @@ from brand.dev import BrandDev, AsyncBrandDev from tests.utils import assert_matches_type from brand.dev.types import ( - BrandFontsResponse, BrandAIQueryResponse, BrandPrefetchResponse, BrandRetrieveResponse, @@ -305,49 +304,6 @@ def test_streaming_response_ai_query(self, client: BrandDev) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_method_fonts(self, client: BrandDev) -> None: - brand = client.brand.fonts( - domain="domain", - ) - assert_matches_type(BrandFontsResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_method_fonts_with_all_params(self, client: BrandDev) -> None: - brand = client.brand.fonts( - domain="domain", - timeout_ms=1000, - ) - assert_matches_type(BrandFontsResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_raw_response_fonts(self, client: BrandDev) -> None: - response = client.brand.with_raw_response.fonts( - domain="domain", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - brand = response.parse() - assert_matches_type(BrandFontsResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_streaming_response_fonts(self, client: BrandDev) -> None: - with client.brand.with_streaming_response.fonts( - domain="domain", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - brand = response.parse() - assert_matches_type(BrandFontsResponse, brand, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_identify_from_transaction(self, client: BrandDev) -> None: @@ -1272,49 +1228,6 @@ async def test_streaming_response_ai_query(self, async_client: AsyncBrandDev) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_fonts(self, async_client: AsyncBrandDev) -> None: - brand = await async_client.brand.fonts( - domain="domain", - ) - assert_matches_type(BrandFontsResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_fonts_with_all_params(self, async_client: AsyncBrandDev) -> None: - brand = await async_client.brand.fonts( - domain="domain", - timeout_ms=1000, - ) - assert_matches_type(BrandFontsResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_raw_response_fonts(self, async_client: AsyncBrandDev) -> None: - response = await async_client.brand.with_raw_response.fonts( - domain="domain", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - brand = await response.parse() - assert_matches_type(BrandFontsResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_streaming_response_fonts(self, async_client: AsyncBrandDev) -> None: - async with async_client.brand.with_streaming_response.fonts( - domain="domain", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - brand = await response.parse() - assert_matches_type(BrandFontsResponse, brand, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_method_identify_from_transaction(self, async_client: AsyncBrandDev) -> None: From af4c2fa317c4a5cd2ef86e6562fe41ff7436daed Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 22:17:13 +0000 Subject: [PATCH 20/82] feat(api): api update --- .stats.yml | 6 +- api.md | 2 - src/brand/dev/resources/brand.py | 162 ------------------ src/brand/dev/types/__init__.py | 2 - .../dev/types/brand_screenshot_params.py | 48 ------ .../dev/types/brand_screenshot_response.py | 27 --- tests/api_resources/test_brand.py | 81 --------- 7 files changed, 3 insertions(+), 325 deletions(-) delete mode 100644 src/brand/dev/types/brand_screenshot_params.py delete mode 100644 src/brand/dev/types/brand_screenshot_response.py diff --git a/.stats.yml b/.stats.yml index c1176570..0198e387 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 19 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-135cee98ff73868b77c6dd17f5d1ccc7cd1e26a8cef266ceba1020346e11a8ef.yml -openapi_spec_hash: d9c1751a0fd999118b6d55d4931212e1 +configured_endpoints: 18 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-73b75de3d3aae732e496f3828fc2c88711969e8abaae060d939da505f36cb3f8.yml +openapi_spec_hash: 30b5dacd44ef41592ba645033b3a1ca3 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/api.md b/api.md index 7a36584a..e81f4bd0 100644 --- a/api.md +++ b/api.md @@ -17,7 +17,6 @@ from brand.dev.types import ( BrandRetrieveByTickerResponse, BrandRetrieveNaicsResponse, BrandRetrieveSimplifiedResponse, - BrandScreenshotResponse, BrandStyleguideResponse, BrandWebScrapeHTMLResponse, BrandWebScrapeImagesResponse, @@ -41,7 +40,6 @@ Methods: - client.brand.retrieve_by_ticker(\*\*params) -> BrandRetrieveByTickerResponse - client.brand.retrieve_naics(\*\*params) -> BrandRetrieveNaicsResponse - client.brand.retrieve_simplified(\*\*params) -> BrandRetrieveSimplifiedResponse -- client.brand.screenshot(\*\*params) -> BrandScreenshotResponse - client.brand.styleguide(\*\*params) -> BrandStyleguideResponse - client.brand.web_scrape_html(\*\*params) -> BrandWebScrapeHTMLResponse - client.brand.web_scrape_images(\*\*params) -> BrandWebScrapeImagesResponse diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index d9b24627..758f92ab 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -12,7 +12,6 @@ brand_prefetch_params, brand_retrieve_params, brand_ai_product_params, - brand_screenshot_params, brand_styleguide_params, brand_ai_products_params, brand_web_scrape_md_params, @@ -43,7 +42,6 @@ from ..types.brand_prefetch_response import BrandPrefetchResponse from ..types.brand_retrieve_response import BrandRetrieveResponse from ..types.brand_ai_product_response import BrandAIProductResponse -from ..types.brand_screenshot_response import BrandScreenshotResponse from ..types.brand_styleguide_response import BrandStyleguideResponse from ..types.brand_ai_products_response import BrandAIProductsResponse from ..types.brand_web_scrape_md_response import BrandWebScrapeMdResponse @@ -2189,80 +2187,6 @@ def retrieve_simplified( cast_to=BrandRetrieveSimplifiedResponse, ) - def screenshot( - self, - *, - direct_url: str | Omit = omit, - domain: str | Omit = omit, - full_screenshot: Literal["true", "false"] | Omit = omit, - page: Literal["login", "signup", "blog", "careers", "pricing", "terms", "privacy", "contact"] | Omit = omit, - prioritize: Literal["speed", "quality"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BrandScreenshotResponse: - """Capture a screenshot of a website. - - Supports both viewport (standard browser - view) and full-page screenshots. Can also screenshot specific page types (login, - pricing, etc.) by using heuristics to find the appropriate URL. Either 'domain' - or 'directUrl' must be provided as a query parameter, but not both. Returns a - URL to the uploaded screenshot image hosted on our CDN. - - Args: - direct_url: A specific URL to screenshot directly, bypassing domain resolution (e.g., - 'https://example.com/pricing'). When provided, the screenshot is taken of this - exact URL. - - domain: Domain name to take screenshot of (e.g., 'example.com', 'google.com'). The - domain will be automatically normalized and validated. - - full_screenshot: Optional parameter to determine screenshot type. If 'true', takes a full page - screenshot capturing all content. If 'false' or not provided, takes a viewport - screenshot (standard browser view). - - page: Optional parameter to specify which page type to screenshot. If provided, the - system will scrape the domain's links and use heuristics to find the most - appropriate URL for the specified page type (30 supported languages). If not - provided, screenshots the main domain landing page. Only applicable when using - 'domain', not 'directUrl'. - - prioritize: Optional parameter to prioritize screenshot capture. If 'speed', optimizes for - faster capture with basic quality. If 'quality', optimizes for higher quality - with longer wait times. Defaults to 'quality' if not provided. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/brand/screenshot", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "direct_url": direct_url, - "domain": domain, - "full_screenshot": full_screenshot, - "page": page, - "prioritize": prioritize, - }, - brand_screenshot_params.BrandScreenshotParams, - ), - ), - cast_to=BrandScreenshotResponse, - ) - def styleguide( self, *, @@ -4649,80 +4573,6 @@ async def retrieve_simplified( cast_to=BrandRetrieveSimplifiedResponse, ) - async def screenshot( - self, - *, - direct_url: str | Omit = omit, - domain: str | Omit = omit, - full_screenshot: Literal["true", "false"] | Omit = omit, - page: Literal["login", "signup", "blog", "careers", "pricing", "terms", "privacy", "contact"] | Omit = omit, - prioritize: Literal["speed", "quality"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BrandScreenshotResponse: - """Capture a screenshot of a website. - - Supports both viewport (standard browser - view) and full-page screenshots. Can also screenshot specific page types (login, - pricing, etc.) by using heuristics to find the appropriate URL. Either 'domain' - or 'directUrl' must be provided as a query parameter, but not both. Returns a - URL to the uploaded screenshot image hosted on our CDN. - - Args: - direct_url: A specific URL to screenshot directly, bypassing domain resolution (e.g., - 'https://example.com/pricing'). When provided, the screenshot is taken of this - exact URL. - - domain: Domain name to take screenshot of (e.g., 'example.com', 'google.com'). The - domain will be automatically normalized and validated. - - full_screenshot: Optional parameter to determine screenshot type. If 'true', takes a full page - screenshot capturing all content. If 'false' or not provided, takes a viewport - screenshot (standard browser view). - - page: Optional parameter to specify which page type to screenshot. If provided, the - system will scrape the domain's links and use heuristics to find the most - appropriate URL for the specified page type (30 supported languages). If not - provided, screenshots the main domain landing page. Only applicable when using - 'domain', not 'directUrl'. - - prioritize: Optional parameter to prioritize screenshot capture. If 'speed', optimizes for - faster capture with basic quality. If 'quality', optimizes for higher quality - with longer wait times. Defaults to 'quality' if not provided. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/brand/screenshot", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "direct_url": direct_url, - "domain": domain, - "full_screenshot": full_screenshot, - "page": page, - "prioritize": prioritize, - }, - brand_screenshot_params.BrandScreenshotParams, - ), - ), - cast_to=BrandScreenshotResponse, - ) - async def styleguide( self, *, @@ -5027,9 +4877,6 @@ def __init__(self, brand: BrandResource) -> None: self.retrieve_simplified = to_raw_response_wrapper( brand.retrieve_simplified, ) - self.screenshot = to_raw_response_wrapper( - brand.screenshot, - ) self.styleguide = to_raw_response_wrapper( brand.styleguide, ) @@ -5090,9 +4937,6 @@ def __init__(self, brand: AsyncBrandResource) -> None: self.retrieve_simplified = async_to_raw_response_wrapper( brand.retrieve_simplified, ) - self.screenshot = async_to_raw_response_wrapper( - brand.screenshot, - ) self.styleguide = async_to_raw_response_wrapper( brand.styleguide, ) @@ -5153,9 +4997,6 @@ def __init__(self, brand: BrandResource) -> None: self.retrieve_simplified = to_streamed_response_wrapper( brand.retrieve_simplified, ) - self.screenshot = to_streamed_response_wrapper( - brand.screenshot, - ) self.styleguide = to_streamed_response_wrapper( brand.styleguide, ) @@ -5216,9 +5057,6 @@ def __init__(self, brand: AsyncBrandResource) -> None: self.retrieve_simplified = async_to_streamed_response_wrapper( brand.retrieve_simplified, ) - self.screenshot = async_to_streamed_response_wrapper( - brand.screenshot, - ) self.styleguide = async_to_streamed_response_wrapper( brand.styleguide, ) diff --git a/src/brand/dev/types/__init__.py b/src/brand/dev/types/__init__.py index 5b665a91..d520d292 100644 --- a/src/brand/dev/types/__init__.py +++ b/src/brand/dev/types/__init__.py @@ -9,11 +9,9 @@ from .brand_ai_query_response import BrandAIQueryResponse as BrandAIQueryResponse from .brand_prefetch_response import BrandPrefetchResponse as BrandPrefetchResponse from .brand_retrieve_response import BrandRetrieveResponse as BrandRetrieveResponse -from .brand_screenshot_params import BrandScreenshotParams as BrandScreenshotParams from .brand_styleguide_params import BrandStyleguideParams as BrandStyleguideParams from .brand_ai_products_params import BrandAIProductsParams as BrandAIProductsParams from .brand_ai_product_response import BrandAIProductResponse as BrandAIProductResponse -from .brand_screenshot_response import BrandScreenshotResponse as BrandScreenshotResponse from .brand_styleguide_response import BrandStyleguideResponse as BrandStyleguideResponse from .brand_ai_products_response import BrandAIProductsResponse as BrandAIProductsResponse from .brand_web_scrape_md_params import BrandWebScrapeMdParams as BrandWebScrapeMdParams diff --git a/src/brand/dev/types/brand_screenshot_params.py b/src/brand/dev/types/brand_screenshot_params.py deleted file mode 100644 index b71ac5ca..00000000 --- a/src/brand/dev/types/brand_screenshot_params.py +++ /dev/null @@ -1,48 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["BrandScreenshotParams"] - - -class BrandScreenshotParams(TypedDict, total=False): - direct_url: Annotated[str, PropertyInfo(alias="directUrl")] - """ - A specific URL to screenshot directly, bypassing domain resolution (e.g., - 'https://example.com/pricing'). When provided, the screenshot is taken of this - exact URL. - """ - - domain: str - """Domain name to take screenshot of (e.g., 'example.com', 'google.com'). - - The domain will be automatically normalized and validated. - """ - - full_screenshot: Annotated[Literal["true", "false"], PropertyInfo(alias="fullScreenshot")] - """Optional parameter to determine screenshot type. - - If 'true', takes a full page screenshot capturing all content. If 'false' or not - provided, takes a viewport screenshot (standard browser view). - """ - - page: Literal["login", "signup", "blog", "careers", "pricing", "terms", "privacy", "contact"] - """Optional parameter to specify which page type to screenshot. - - If provided, the system will scrape the domain's links and use heuristics to - find the most appropriate URL for the specified page type (30 supported - languages). If not provided, screenshots the main domain landing page. Only - applicable when using 'domain', not 'directUrl'. - """ - - prioritize: Literal["speed", "quality"] - """Optional parameter to prioritize screenshot capture. - - If 'speed', optimizes for faster capture with basic quality. If 'quality', - optimizes for higher quality with longer wait times. Defaults to 'quality' if - not provided. - """ diff --git a/src/brand/dev/types/brand_screenshot_response.py b/src/brand/dev/types/brand_screenshot_response.py deleted file mode 100644 index c43ae741..00000000 --- a/src/brand/dev/types/brand_screenshot_response.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["BrandScreenshotResponse"] - - -class BrandScreenshotResponse(BaseModel): - code: Optional[int] = None - """HTTP status code""" - - domain: Optional[str] = None - """The normalized domain that was processed""" - - screenshot: Optional[str] = None - """Public URL of the uploaded screenshot image""" - - screenshot_type: Optional[Literal["viewport", "fullPage"]] = FieldInfo(alias="screenshotType", default=None) - """Type of screenshot that was captured""" - - status: Optional[str] = None - """Status of the response, e.g., 'ok'""" diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index 7fd6aab0..bc6e87b8 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -15,7 +15,6 @@ BrandRetrieveResponse, BrandAIProductResponse, BrandAIProductsResponse, - BrandScreenshotResponse, BrandStyleguideResponse, BrandWebScrapeMdResponse, BrandRetrieveNaicsResponse, @@ -710,46 +709,6 @@ def test_streaming_response_retrieve_simplified(self, client: BrandDev) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_method_screenshot(self, client: BrandDev) -> None: - brand = client.brand.screenshot() - assert_matches_type(BrandScreenshotResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_method_screenshot_with_all_params(self, client: BrandDev) -> None: - brand = client.brand.screenshot( - direct_url="https://example.com", - domain="domain", - full_screenshot="true", - page="login", - prioritize="speed", - ) - assert_matches_type(BrandScreenshotResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_raw_response_screenshot(self, client: BrandDev) -> None: - response = client.brand.with_raw_response.screenshot() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - brand = response.parse() - assert_matches_type(BrandScreenshotResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_streaming_response_screenshot(self, client: BrandDev) -> None: - with client.brand.with_streaming_response.screenshot() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - brand = response.parse() - assert_matches_type(BrandScreenshotResponse, brand, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_styleguide(self, client: BrandDev) -> None: @@ -1634,46 +1593,6 @@ async def test_streaming_response_retrieve_simplified(self, async_client: AsyncB assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_screenshot(self, async_client: AsyncBrandDev) -> None: - brand = await async_client.brand.screenshot() - assert_matches_type(BrandScreenshotResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_screenshot_with_all_params(self, async_client: AsyncBrandDev) -> None: - brand = await async_client.brand.screenshot( - direct_url="https://example.com", - domain="domain", - full_screenshot="true", - page="login", - prioritize="speed", - ) - assert_matches_type(BrandScreenshotResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_raw_response_screenshot(self, async_client: AsyncBrandDev) -> None: - response = await async_client.brand.with_raw_response.screenshot() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - brand = await response.parse() - assert_matches_type(BrandScreenshotResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_streaming_response_screenshot(self, async_client: AsyncBrandDev) -> None: - async with async_client.brand.with_streaming_response.screenshot() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - brand = await response.parse() - assert_matches_type(BrandScreenshotResponse, brand, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_method_styleguide(self, async_client: AsyncBrandDev) -> None: From b494e66bdee0f1c447ee587836aa74c7955da0aa Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 23:10:42 +0000 Subject: [PATCH 21/82] feat(api): api update --- .stats.yml | 6 +- api.md | 2 - src/brand/dev/resources/brand.py | 128 ------ src/brand/dev/types/__init__.py | 2 - .../dev/types/brand_styleguide_params.py | 30 -- .../dev/types/brand_styleguide_response.py | 427 ------------------ tests/api_resources/test_brand.py | 77 ---- 7 files changed, 3 insertions(+), 669 deletions(-) delete mode 100644 src/brand/dev/types/brand_styleguide_params.py delete mode 100644 src/brand/dev/types/brand_styleguide_response.py diff --git a/.stats.yml b/.stats.yml index 0198e387..957b33f1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 18 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-73b75de3d3aae732e496f3828fc2c88711969e8abaae060d939da505f36cb3f8.yml -openapi_spec_hash: 30b5dacd44ef41592ba645033b3a1ca3 +configured_endpoints: 17 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-db86584e77523e133c60fce82fe9c3a4957efc0ad317fd0aed1b928c7f0b2f8a.yml +openapi_spec_hash: dfc39b2ad61ed889019ffe6fcb52bcce config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/api.md b/api.md index e81f4bd0..123478a0 100644 --- a/api.md +++ b/api.md @@ -17,7 +17,6 @@ from brand.dev.types import ( BrandRetrieveByTickerResponse, BrandRetrieveNaicsResponse, BrandRetrieveSimplifiedResponse, - BrandStyleguideResponse, BrandWebScrapeHTMLResponse, BrandWebScrapeImagesResponse, BrandWebScrapeMdResponse, @@ -40,7 +39,6 @@ Methods: - client.brand.retrieve_by_ticker(\*\*params) -> BrandRetrieveByTickerResponse - client.brand.retrieve_naics(\*\*params) -> BrandRetrieveNaicsResponse - client.brand.retrieve_simplified(\*\*params) -> BrandRetrieveSimplifiedResponse -- client.brand.styleguide(\*\*params) -> BrandStyleguideResponse - client.brand.web_scrape_html(\*\*params) -> BrandWebScrapeHTMLResponse - client.brand.web_scrape_images(\*\*params) -> BrandWebScrapeImagesResponse - client.brand.web_scrape_md(\*\*params) -> BrandWebScrapeMdResponse diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index 758f92ab..48c8a051 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -12,7 +12,6 @@ brand_prefetch_params, brand_retrieve_params, brand_ai_product_params, - brand_styleguide_params, brand_ai_products_params, brand_web_scrape_md_params, brand_retrieve_naics_params, @@ -42,7 +41,6 @@ from ..types.brand_prefetch_response import BrandPrefetchResponse from ..types.brand_retrieve_response import BrandRetrieveResponse from ..types.brand_ai_product_response import BrandAIProductResponse -from ..types.brand_styleguide_response import BrandStyleguideResponse from ..types.brand_ai_products_response import BrandAIProductsResponse from ..types.brand_web_scrape_md_response import BrandWebScrapeMdResponse from ..types.brand_retrieve_naics_response import BrandRetrieveNaicsResponse @@ -2187,63 +2185,6 @@ def retrieve_simplified( cast_to=BrandRetrieveSimplifiedResponse, ) - def styleguide( - self, - *, - direct_url: str | Omit = omit, - domain: str | Omit = omit, - timeout_ms: int | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BrandStyleguideResponse: - """ - Automatically extract comprehensive design system information from a brand's - website including colors, typography, spacing, shadows, and UI components. - Either 'domain' or 'directUrl' must be provided as a query parameter, but not - both. - - Args: - direct_url: A specific URL to fetch the styleguide from directly, bypassing domain - resolution (e.g., 'https://example.com/design-system'). - - domain: Domain name to extract styleguide from (e.g., 'example.com', 'google.com'). The - domain will be automatically normalized and validated. - - timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer - than this value, it will be aborted with a 408 status code. Maximum allowed - value is 300000ms (5 minutes). - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/brand/styleguide", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "direct_url": direct_url, - "domain": domain, - "timeout_ms": timeout_ms, - }, - brand_styleguide_params.BrandStyleguideParams, - ), - ), - cast_to=BrandStyleguideResponse, - ) - def web_scrape_html( self, *, @@ -4573,63 +4514,6 @@ async def retrieve_simplified( cast_to=BrandRetrieveSimplifiedResponse, ) - async def styleguide( - self, - *, - direct_url: str | Omit = omit, - domain: str | Omit = omit, - timeout_ms: int | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BrandStyleguideResponse: - """ - Automatically extract comprehensive design system information from a brand's - website including colors, typography, spacing, shadows, and UI components. - Either 'domain' or 'directUrl' must be provided as a query parameter, but not - both. - - Args: - direct_url: A specific URL to fetch the styleguide from directly, bypassing domain - resolution (e.g., 'https://example.com/design-system'). - - domain: Domain name to extract styleguide from (e.g., 'example.com', 'google.com'). The - domain will be automatically normalized and validated. - - timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer - than this value, it will be aborted with a 408 status code. Maximum allowed - value is 300000ms (5 minutes). - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/brand/styleguide", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "direct_url": direct_url, - "domain": domain, - "timeout_ms": timeout_ms, - }, - brand_styleguide_params.BrandStyleguideParams, - ), - ), - cast_to=BrandStyleguideResponse, - ) - async def web_scrape_html( self, *, @@ -4877,9 +4761,6 @@ def __init__(self, brand: BrandResource) -> None: self.retrieve_simplified = to_raw_response_wrapper( brand.retrieve_simplified, ) - self.styleguide = to_raw_response_wrapper( - brand.styleguide, - ) self.web_scrape_html = to_raw_response_wrapper( brand.web_scrape_html, ) @@ -4937,9 +4818,6 @@ def __init__(self, brand: AsyncBrandResource) -> None: self.retrieve_simplified = async_to_raw_response_wrapper( brand.retrieve_simplified, ) - self.styleguide = async_to_raw_response_wrapper( - brand.styleguide, - ) self.web_scrape_html = async_to_raw_response_wrapper( brand.web_scrape_html, ) @@ -4997,9 +4875,6 @@ def __init__(self, brand: BrandResource) -> None: self.retrieve_simplified = to_streamed_response_wrapper( brand.retrieve_simplified, ) - self.styleguide = to_streamed_response_wrapper( - brand.styleguide, - ) self.web_scrape_html = to_streamed_response_wrapper( brand.web_scrape_html, ) @@ -5057,9 +4932,6 @@ def __init__(self, brand: AsyncBrandResource) -> None: self.retrieve_simplified = async_to_streamed_response_wrapper( brand.retrieve_simplified, ) - self.styleguide = async_to_streamed_response_wrapper( - brand.styleguide, - ) self.web_scrape_html = async_to_streamed_response_wrapper( brand.web_scrape_html, ) diff --git a/src/brand/dev/types/__init__.py b/src/brand/dev/types/__init__.py index d520d292..bb12a4f0 100644 --- a/src/brand/dev/types/__init__.py +++ b/src/brand/dev/types/__init__.py @@ -9,10 +9,8 @@ from .brand_ai_query_response import BrandAIQueryResponse as BrandAIQueryResponse from .brand_prefetch_response import BrandPrefetchResponse as BrandPrefetchResponse from .brand_retrieve_response import BrandRetrieveResponse as BrandRetrieveResponse -from .brand_styleguide_params import BrandStyleguideParams as BrandStyleguideParams from .brand_ai_products_params import BrandAIProductsParams as BrandAIProductsParams from .brand_ai_product_response import BrandAIProductResponse as BrandAIProductResponse -from .brand_styleguide_response import BrandStyleguideResponse as BrandStyleguideResponse from .brand_ai_products_response import BrandAIProductsResponse as BrandAIProductsResponse from .brand_web_scrape_md_params import BrandWebScrapeMdParams as BrandWebScrapeMdParams from .brand_retrieve_naics_params import BrandRetrieveNaicsParams as BrandRetrieveNaicsParams diff --git a/src/brand/dev/types/brand_styleguide_params.py b/src/brand/dev/types/brand_styleguide_params.py deleted file mode 100644 index 9c858b54..00000000 --- a/src/brand/dev/types/brand_styleguide_params.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["BrandStyleguideParams"] - - -class BrandStyleguideParams(TypedDict, total=False): - direct_url: Annotated[str, PropertyInfo(alias="directUrl")] - """ - A specific URL to fetch the styleguide from directly, bypassing domain - resolution (e.g., 'https://example.com/design-system'). - """ - - domain: str - """Domain name to extract styleguide from (e.g., 'example.com', 'google.com'). - - The domain will be automatically normalized and validated. - """ - - timeout_ms: Annotated[int, PropertyInfo(alias="timeoutMS")] - """Optional timeout in milliseconds for the request. - - If the request takes longer than this value, it will be aborted with a 408 - status code. Maximum allowed value is 300000ms (5 minutes). - """ diff --git a/src/brand/dev/types/brand_styleguide_response.py b/src/brand/dev/types/brand_styleguide_response.py deleted file mode 100644 index 95102038..00000000 --- a/src/brand/dev/types/brand_styleguide_response.py +++ /dev/null @@ -1,427 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, List, Optional -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = [ - "BrandStyleguideResponse", - "Styleguide", - "StyleguideColors", - "StyleguideComponents", - "StyleguideComponentsButton", - "StyleguideComponentsButtonLink", - "StyleguideComponentsButtonPrimary", - "StyleguideComponentsButtonSecondary", - "StyleguideComponentsCard", - "StyleguideElementSpacing", - "StyleguideFontLinks", - "StyleguideShadows", - "StyleguideTypography", - "StyleguideTypographyHeadings", - "StyleguideTypographyHeadingsH1", - "StyleguideTypographyHeadingsH2", - "StyleguideTypographyHeadingsH3", - "StyleguideTypographyHeadingsH4", - "StyleguideTypographyP", -] - - -class StyleguideColors(BaseModel): - """Primary colors used on the website""" - - accent: str - """Accent color (hex format)""" - - background: str - """Background color (hex format)""" - - text: str - """Text color (hex format)""" - - -class StyleguideComponentsButtonLink(BaseModel): - background_color: str = FieldInfo(alias="backgroundColor") - - border_color: str = FieldInfo(alias="borderColor") - """ - Border color as CSS hex (#RRGGBB or #RRGGBBAA when computed border-color has - alpha) - """ - - border_radius: str = FieldInfo(alias="borderRadius") - - border_style: str = FieldInfo(alias="borderStyle") - - border_width: str = FieldInfo(alias="borderWidth") - - box_shadow: str = FieldInfo(alias="boxShadow") - """Computed box-shadow (comma-separated layers when present)""" - - color: str - - css: str - """Ready-to-use CSS declaration block for this component style""" - - font_size: str = FieldInfo(alias="fontSize") - - font_weight: float = FieldInfo(alias="fontWeight") - - min_height: str = FieldInfo(alias="minHeight") - """Sampled minimum height of the button box (typically px)""" - - min_width: str = FieldInfo(alias="minWidth") - """Sampled minimum width of the button box (typically px)""" - - padding: str - - text_decoration: str = FieldInfo(alias="textDecoration") - - font_fallbacks: Optional[List[str]] = FieldInfo(alias="fontFallbacks", default=None) - """Full ordered font list from computed font-family""" - - font_family: Optional[str] = FieldInfo(alias="fontFamily", default=None) - """Primary button typeface (first in fontFallbacks)""" - - text_decoration_color: Optional[str] = FieldInfo(alias="textDecorationColor", default=None) - """Hex color of the underline when it differs from the text color""" - - -class StyleguideComponentsButtonPrimary(BaseModel): - background_color: str = FieldInfo(alias="backgroundColor") - - border_color: str = FieldInfo(alias="borderColor") - """ - Border color as CSS hex (#RRGGBB or #RRGGBBAA when computed border-color has - alpha) - """ - - border_radius: str = FieldInfo(alias="borderRadius") - - border_style: str = FieldInfo(alias="borderStyle") - - border_width: str = FieldInfo(alias="borderWidth") - - box_shadow: str = FieldInfo(alias="boxShadow") - """Computed box-shadow (comma-separated layers when present)""" - - color: str - - css: str - """Ready-to-use CSS declaration block for this component style""" - - font_size: str = FieldInfo(alias="fontSize") - - font_weight: float = FieldInfo(alias="fontWeight") - - min_height: str = FieldInfo(alias="minHeight") - """Sampled minimum height of the button box (typically px)""" - - min_width: str = FieldInfo(alias="minWidth") - """Sampled minimum width of the button box (typically px)""" - - padding: str - - text_decoration: str = FieldInfo(alias="textDecoration") - - font_fallbacks: Optional[List[str]] = FieldInfo(alias="fontFallbacks", default=None) - """Full ordered font list from computed font-family""" - - font_family: Optional[str] = FieldInfo(alias="fontFamily", default=None) - """Primary button typeface (first in fontFallbacks)""" - - text_decoration_color: Optional[str] = FieldInfo(alias="textDecorationColor", default=None) - """Hex color of the underline when it differs from the text color""" - - -class StyleguideComponentsButtonSecondary(BaseModel): - background_color: str = FieldInfo(alias="backgroundColor") - - border_color: str = FieldInfo(alias="borderColor") - """ - Border color as CSS hex (#RRGGBB or #RRGGBBAA when computed border-color has - alpha) - """ - - border_radius: str = FieldInfo(alias="borderRadius") - - border_style: str = FieldInfo(alias="borderStyle") - - border_width: str = FieldInfo(alias="borderWidth") - - box_shadow: str = FieldInfo(alias="boxShadow") - """Computed box-shadow (comma-separated layers when present)""" - - color: str - - css: str - """Ready-to-use CSS declaration block for this component style""" - - font_size: str = FieldInfo(alias="fontSize") - - font_weight: float = FieldInfo(alias="fontWeight") - - min_height: str = FieldInfo(alias="minHeight") - """Sampled minimum height of the button box (typically px)""" - - min_width: str = FieldInfo(alias="minWidth") - """Sampled minimum width of the button box (typically px)""" - - padding: str - - text_decoration: str = FieldInfo(alias="textDecoration") - - font_fallbacks: Optional[List[str]] = FieldInfo(alias="fontFallbacks", default=None) - """Full ordered font list from computed font-family""" - - font_family: Optional[str] = FieldInfo(alias="fontFamily", default=None) - """Primary button typeface (first in fontFallbacks)""" - - text_decoration_color: Optional[str] = FieldInfo(alias="textDecorationColor", default=None) - """Hex color of the underline when it differs from the text color""" - - -class StyleguideComponentsButton(BaseModel): - """Button component styles""" - - link: Optional[StyleguideComponentsButtonLink] = None - - primary: Optional[StyleguideComponentsButtonPrimary] = None - - secondary: Optional[StyleguideComponentsButtonSecondary] = None - - -class StyleguideComponentsCard(BaseModel): - """Card component style""" - - background_color: str = FieldInfo(alias="backgroundColor") - - border_color: str = FieldInfo(alias="borderColor") - """ - Border color as CSS hex (#RRGGBB or #RRGGBBAA when computed border-color has - alpha) - """ - - border_radius: str = FieldInfo(alias="borderRadius") - - border_style: str = FieldInfo(alias="borderStyle") - - border_width: str = FieldInfo(alias="borderWidth") - - box_shadow: str = FieldInfo(alias="boxShadow") - - css: str - """Ready-to-use CSS declaration block for this component style""" - - padding: str - - text_color: str = FieldInfo(alias="textColor") - - -class StyleguideComponents(BaseModel): - """UI component styles""" - - button: StyleguideComponentsButton - """Button component styles""" - - card: Optional[StyleguideComponentsCard] = None - """Card component style""" - - -class StyleguideElementSpacing(BaseModel): - """Spacing system used on the website""" - - lg: str - - md: str - - sm: str - - xl: str - - xs: str - - -class StyleguideFontLinks(BaseModel): - files: Dict[str, str] - """Upright font files keyed by weight string (e.g. - - "400" for regular, "500", "700"). Values are absolute URLs. - """ - - type: Literal["google", "custom"] - - category: Optional[str] = None - """Google Fonts category when type is google (e.g. - - sans-serif, serif, monospace, display, handwriting). Omitted for custom fonts - when unknown. - """ - - display_name: Optional[str] = FieldInfo(alias="displayName", default=None) - """ - Present when type is custom: human-readable name derived from the fontLinks key - (strip build/hash suffixes, split camelCase / PascalCase, normalize separators). - Google entries omit this. - """ - - -class StyleguideShadows(BaseModel): - """Shadow styles used on the website""" - - inner: str - - lg: str - - md: str - - sm: str - - xl: str - - -class StyleguideTypographyHeadingsH1(BaseModel): - font_fallbacks: List[str] = FieldInfo(alias="fontFallbacks") - """Full ordered font list from resolved computed font-family""" - - font_family: str = FieldInfo(alias="fontFamily") - """Primary face (first family in the computed stack)""" - - font_size: str = FieldInfo(alias="fontSize") - - font_weight: float = FieldInfo(alias="fontWeight") - - letter_spacing: str = FieldInfo(alias="letterSpacing") - - line_height: str = FieldInfo(alias="lineHeight") - - -class StyleguideTypographyHeadingsH2(BaseModel): - font_fallbacks: List[str] = FieldInfo(alias="fontFallbacks") - """Full ordered font list from resolved computed font-family""" - - font_family: str = FieldInfo(alias="fontFamily") - """Primary face (first family in the computed stack)""" - - font_size: str = FieldInfo(alias="fontSize") - - font_weight: float = FieldInfo(alias="fontWeight") - - letter_spacing: str = FieldInfo(alias="letterSpacing") - - line_height: str = FieldInfo(alias="lineHeight") - - -class StyleguideTypographyHeadingsH3(BaseModel): - font_fallbacks: List[str] = FieldInfo(alias="fontFallbacks") - """Full ordered font list from resolved computed font-family""" - - font_family: str = FieldInfo(alias="fontFamily") - """Primary face (first family in the computed stack)""" - - font_size: str = FieldInfo(alias="fontSize") - - font_weight: float = FieldInfo(alias="fontWeight") - - letter_spacing: str = FieldInfo(alias="letterSpacing") - - line_height: str = FieldInfo(alias="lineHeight") - - -class StyleguideTypographyHeadingsH4(BaseModel): - font_fallbacks: List[str] = FieldInfo(alias="fontFallbacks") - """Full ordered font list from resolved computed font-family""" - - font_family: str = FieldInfo(alias="fontFamily") - """Primary face (first family in the computed stack)""" - - font_size: str = FieldInfo(alias="fontSize") - - font_weight: float = FieldInfo(alias="fontWeight") - - letter_spacing: str = FieldInfo(alias="letterSpacing") - - line_height: str = FieldInfo(alias="lineHeight") - - -class StyleguideTypographyHeadings(BaseModel): - """Heading styles""" - - h1: Optional[StyleguideTypographyHeadingsH1] = None - - h2: Optional[StyleguideTypographyHeadingsH2] = None - - h3: Optional[StyleguideTypographyHeadingsH3] = None - - h4: Optional[StyleguideTypographyHeadingsH4] = None - - -class StyleguideTypographyP(BaseModel): - font_fallbacks: List[str] = FieldInfo(alias="fontFallbacks") - """Full ordered font list from resolved computed font-family""" - - font_family: str = FieldInfo(alias="fontFamily") - """Primary face (first family in the computed stack)""" - - font_size: str = FieldInfo(alias="fontSize") - - font_weight: float = FieldInfo(alias="fontWeight") - - letter_spacing: str = FieldInfo(alias="letterSpacing") - - line_height: str = FieldInfo(alias="lineHeight") - - -class StyleguideTypography(BaseModel): - """Typography styles used on the website""" - - headings: StyleguideTypographyHeadings - """Heading styles""" - - p: Optional[StyleguideTypographyP] = None - - -class Styleguide(BaseModel): - """Comprehensive styleguide data extracted from the website""" - - colors: StyleguideColors - """Primary colors used on the website""" - - components: StyleguideComponents - """UI component styles""" - - element_spacing: StyleguideElementSpacing = FieldInfo(alias="elementSpacing") - """Spacing system used on the website""" - - font_links: Dict[str, StyleguideFontLinks] = FieldInfo(alias="fontLinks") - """ - Font assets keyed by family name as it appears in fontFamily/fontFallbacks - (non-generic names only). Clients match typography.fontFamily / fontWeight or - button styles to pick a file URL from files. - """ - - mode: Literal["light", "dark"] - """The primary color mode of the website design""" - - shadows: StyleguideShadows - """Shadow styles used on the website""" - - typography: StyleguideTypography - """Typography styles used on the website""" - - -class BrandStyleguideResponse(BaseModel): - code: Optional[int] = None - """HTTP status code""" - - domain: Optional[str] = None - """The normalized domain that was processed""" - - status: Optional[str] = None - """Status of the response, e.g., 'ok'""" - - styleguide: Optional[Styleguide] = None - """Comprehensive styleguide data extracted from the website""" diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index bc6e87b8..5b76b204 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -15,7 +15,6 @@ BrandRetrieveResponse, BrandAIProductResponse, BrandAIProductsResponse, - BrandStyleguideResponse, BrandWebScrapeMdResponse, BrandRetrieveNaicsResponse, BrandWebScrapeHTMLResponse, @@ -709,44 +708,6 @@ def test_streaming_response_retrieve_simplified(self, client: BrandDev) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_method_styleguide(self, client: BrandDev) -> None: - brand = client.brand.styleguide() - assert_matches_type(BrandStyleguideResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_method_styleguide_with_all_params(self, client: BrandDev) -> None: - brand = client.brand.styleguide( - direct_url="https://example.com", - domain="domain", - timeout_ms=1000, - ) - assert_matches_type(BrandStyleguideResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_raw_response_styleguide(self, client: BrandDev) -> None: - response = client.brand.with_raw_response.styleguide() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - brand = response.parse() - assert_matches_type(BrandStyleguideResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_streaming_response_styleguide(self, client: BrandDev) -> None: - with client.brand.with_streaming_response.styleguide() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - brand = response.parse() - assert_matches_type(BrandStyleguideResponse, brand, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_web_scrape_html(self, client: BrandDev) -> None: @@ -1593,44 +1554,6 @@ async def test_streaming_response_retrieve_simplified(self, async_client: AsyncB assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_styleguide(self, async_client: AsyncBrandDev) -> None: - brand = await async_client.brand.styleguide() - assert_matches_type(BrandStyleguideResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_styleguide_with_all_params(self, async_client: AsyncBrandDev) -> None: - brand = await async_client.brand.styleguide( - direct_url="https://example.com", - domain="domain", - timeout_ms=1000, - ) - assert_matches_type(BrandStyleguideResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_raw_response_styleguide(self, async_client: AsyncBrandDev) -> None: - response = await async_client.brand.with_raw_response.styleguide() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - brand = await response.parse() - assert_matches_type(BrandStyleguideResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_streaming_response_styleguide(self, async_client: AsyncBrandDev) -> None: - async with async_client.brand.with_streaming_response.styleguide() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - brand = await response.parse() - assert_matches_type(BrandStyleguideResponse, brand, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_method_web_scrape_html(self, async_client: AsyncBrandDev) -> None: From b4672ab67f11664113177977497c75e6c5ce82f8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 23:45:22 +0000 Subject: [PATCH 22/82] feat(api): api update --- .stats.yml | 4 +- src/brand/dev/resources/brand.py | 62 +++++++------------ .../brand_identify_from_transaction_params.py | 1 - .../types/brand_retrieve_by_name_params.py | 6 +- 4 files changed, 29 insertions(+), 44 deletions(-) diff --git a/.stats.yml b/.stats.yml index 957b33f1..d1e595cf 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 17 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-db86584e77523e133c60fce82fe9c3a4957efc0ad317fd0aed1b928c7f0b2f8a.yml -openapi_spec_hash: dfc39b2ad61ed889019ffe6fcb52bcce +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-d2e5adb24c5fcf4801996dc77d972711ae67b658a8d2a0ea8e18bb0856489b90.yml +openapi_spec_hash: 3796c471b7905708bfc8ea905abff5a5 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index 48c8a051..37a8f67c 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -869,7 +869,6 @@ def identify_from_transaction( high_confidence_only: When set to true, the API will perform an additional verification steps to ensure the identified brand matches the transaction with high confidence. - Defaults to false. max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of @@ -1155,9 +1154,8 @@ def retrieve_by_email( ) -> BrandRetrieveByEmailResponse: """ Retrieve brand information using an email address while detecting disposable and - free email addresses. This endpoint extracts the domain from the email address - and returns brand data for that domain. Disposable and free email addresses - (like gmail.com, yahoo.com) will throw a 422 error. + free email addresses. Disposable and free email addresses (like gmail.com, + yahoo.com) will throw a 422 error. Args: email: Email address to retrieve brand data for (e.g., 'contact@example.com'). The @@ -1340,8 +1338,7 @@ def retrieve_by_isin( ) -> BrandRetrieveByIsinResponse: """ Retrieve brand information using an ISIN (International Securities - Identification Number). This endpoint looks up the company associated with the - ISIN and returns its brand data. + Identification Number). Args: isin: ISIN (International Securities Identification Number) to retrieve brand data for @@ -1764,17 +1761,15 @@ def retrieve_by_name( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandRetrieveByNameResponse: - """Retrieve brand information using a company name. - - This endpoint searches for the - company by name and returns its brand data. + """ + Retrieve brand information using a company name. Args: name: Company name to retrieve brand data for (e.g., 'Apple Inc', 'Microsoft Corporation'). Must be 3-30 characters. - country_gl: Optional country code (GL parameter) to specify the country. This affects the - geographic location used for search queries. + country_gl: Optional country code hint (GL parameter) to specify the country for the company + name. force_language: Optional parameter to force the language of the retrieved brand data. @@ -2026,10 +2021,8 @@ def retrieve_by_ticker( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandRetrieveByTickerResponse: - """Retrieve brand information using a stock ticker symbol. - - This endpoint looks up - the company associated with the ticker and returns its brand data. + """ + Retrieve brand information using a stock ticker symbol. Args: ticker: Stock ticker symbol to retrieve brand data for (e.g., 'AAPL', 'GOOGL', 'BRK.A'). @@ -2149,8 +2142,8 @@ def retrieve_simplified( ) -> BrandRetrieveSimplifiedResponse: """ Returns a simplified version of brand data containing only essential - information: domain, title, colors, logos, and backdrops. This endpoint is - optimized for faster responses and reduced data transfer. + information: domain, title, colors, logos, and backdrops. Optimized for faster + responses and reduced data transfer. Args: domain: Domain name to retrieve simplified brand data for @@ -2352,7 +2345,7 @@ def web_scrape_sitemap( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandWebScrapeSitemapResponse: """ - Crawl an entire website's sitemap and return all discovered page URLs + Crawl an entire website's sitemap and return all discovered page URLs. Args: domain: Domain to build a sitemap for @@ -3198,7 +3191,6 @@ async def identify_from_transaction( high_confidence_only: When set to true, the API will perform an additional verification steps to ensure the identified brand matches the transaction with high confidence. - Defaults to false. max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of @@ -3484,9 +3476,8 @@ async def retrieve_by_email( ) -> BrandRetrieveByEmailResponse: """ Retrieve brand information using an email address while detecting disposable and - free email addresses. This endpoint extracts the domain from the email address - and returns brand data for that domain. Disposable and free email addresses - (like gmail.com, yahoo.com) will throw a 422 error. + free email addresses. Disposable and free email addresses (like gmail.com, + yahoo.com) will throw a 422 error. Args: email: Email address to retrieve brand data for (e.g., 'contact@example.com'). The @@ -3669,8 +3660,7 @@ async def retrieve_by_isin( ) -> BrandRetrieveByIsinResponse: """ Retrieve brand information using an ISIN (International Securities - Identification Number). This endpoint looks up the company associated with the - ISIN and returns its brand data. + Identification Number). Args: isin: ISIN (International Securities Identification Number) to retrieve brand data for @@ -4093,17 +4083,15 @@ async def retrieve_by_name( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandRetrieveByNameResponse: - """Retrieve brand information using a company name. - - This endpoint searches for the - company by name and returns its brand data. + """ + Retrieve brand information using a company name. Args: name: Company name to retrieve brand data for (e.g., 'Apple Inc', 'Microsoft Corporation'). Must be 3-30 characters. - country_gl: Optional country code (GL parameter) to specify the country. This affects the - geographic location used for search queries. + country_gl: Optional country code hint (GL parameter) to specify the country for the company + name. force_language: Optional parameter to force the language of the retrieved brand data. @@ -4355,10 +4343,8 @@ async def retrieve_by_ticker( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandRetrieveByTickerResponse: - """Retrieve brand information using a stock ticker symbol. - - This endpoint looks up - the company associated with the ticker and returns its brand data. + """ + Retrieve brand information using a stock ticker symbol. Args: ticker: Stock ticker symbol to retrieve brand data for (e.g., 'AAPL', 'GOOGL', 'BRK.A'). @@ -4478,8 +4464,8 @@ async def retrieve_simplified( ) -> BrandRetrieveSimplifiedResponse: """ Returns a simplified version of brand data containing only essential - information: domain, title, colors, logos, and backdrops. This endpoint is - optimized for faster responses and reduced data transfer. + information: domain, title, colors, logos, and backdrops. Optimized for faster + responses and reduced data transfer. Args: domain: Domain name to retrieve simplified brand data for @@ -4683,7 +4669,7 @@ async def web_scrape_sitemap( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandWebScrapeSitemapResponse: """ - Crawl an entire website's sitemap and return all discovered page URLs + Crawl an entire website's sitemap and return all discovered page URLs. Args: domain: Domain to build a sitemap for diff --git a/src/brand/dev/types/brand_identify_from_transaction_params.py b/src/brand/dev/types/brand_identify_from_transaction_params.py index bd3f5a8f..e6ecadd2 100644 --- a/src/brand/dev/types/brand_identify_from_transaction_params.py +++ b/src/brand/dev/types/brand_identify_from_transaction_params.py @@ -390,7 +390,6 @@ class BrandIdentifyFromTransactionParams(TypedDict, total=False): """ When set to true, the API will perform an additional verification steps to ensure the identified brand matches the transaction with high confidence. - Defaults to false. """ max_speed: Annotated[bool, PropertyInfo(alias="maxSpeed")] diff --git a/src/brand/dev/types/brand_retrieve_by_name_params.py b/src/brand/dev/types/brand_retrieve_by_name_params.py index 31a9eefe..d7742236 100644 --- a/src/brand/dev/types/brand_retrieve_by_name_params.py +++ b/src/brand/dev/types/brand_retrieve_by_name_params.py @@ -257,9 +257,9 @@ class BrandRetrieveByNameParams(TypedDict, total=False): "zm", "zw", ] - """Optional country code (GL parameter) to specify the country. - - This affects the geographic location used for search queries. + """ + Optional country code hint (GL parameter) to specify the country for the company + name. """ force_language: Literal[ From a04e9389794725781408dc53d635226406d89cb2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 00:09:14 +0000 Subject: [PATCH 23/82] feat(api): api update --- .stats.yml | 6 +- api.md | 2 - src/brand/dev/resources/brand.py | 132 ------------------ src/brand/dev/types/__init__.py | 2 - .../dev/types/brand_retrieve_naics_params.py | 34 ----- .../types/brand_retrieve_naics_response.py | 33 ----- tests/api_resources/test_brand.py | 91 ------------ 7 files changed, 3 insertions(+), 297 deletions(-) delete mode 100644 src/brand/dev/types/brand_retrieve_naics_params.py delete mode 100644 src/brand/dev/types/brand_retrieve_naics_response.py diff --git a/.stats.yml b/.stats.yml index d1e595cf..f73f1bc1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 17 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-d2e5adb24c5fcf4801996dc77d972711ae67b658a8d2a0ea8e18bb0856489b90.yml -openapi_spec_hash: 3796c471b7905708bfc8ea905abff5a5 +configured_endpoints: 16 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-071c7e9274f7e2f76732f22559fe8b4b75043a73c2c4af74ad2cc429d4bae357.yml +openapi_spec_hash: e7c47fbf61de6e2e9339f1dbb428cb8e config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/api.md b/api.md index 123478a0..7f3b9937 100644 --- a/api.md +++ b/api.md @@ -15,7 +15,6 @@ from brand.dev.types import ( BrandRetrieveByIsinResponse, BrandRetrieveByNameResponse, BrandRetrieveByTickerResponse, - BrandRetrieveNaicsResponse, BrandRetrieveSimplifiedResponse, BrandWebScrapeHTMLResponse, BrandWebScrapeImagesResponse, @@ -37,7 +36,6 @@ Methods: - client.brand.retrieve_by_isin(\*\*params) -> BrandRetrieveByIsinResponse - client.brand.retrieve_by_name(\*\*params) -> BrandRetrieveByNameResponse - client.brand.retrieve_by_ticker(\*\*params) -> BrandRetrieveByTickerResponse -- client.brand.retrieve_naics(\*\*params) -> BrandRetrieveNaicsResponse - client.brand.retrieve_simplified(\*\*params) -> BrandRetrieveSimplifiedResponse - client.brand.web_scrape_html(\*\*params) -> BrandWebScrapeHTMLResponse - client.brand.web_scrape_images(\*\*params) -> BrandWebScrapeImagesResponse diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index 37a8f67c..cb18c074 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -14,7 +14,6 @@ brand_ai_product_params, brand_ai_products_params, brand_web_scrape_md_params, - brand_retrieve_naics_params, brand_web_scrape_html_params, brand_retrieve_by_isin_params, brand_retrieve_by_name_params, @@ -43,7 +42,6 @@ from ..types.brand_ai_product_response import BrandAIProductResponse from ..types.brand_ai_products_response import BrandAIProductsResponse from ..types.brand_web_scrape_md_response import BrandWebScrapeMdResponse -from ..types.brand_retrieve_naics_response import BrandRetrieveNaicsResponse from ..types.brand_web_scrape_html_response import BrandWebScrapeHTMLResponse from ..types.brand_retrieve_by_isin_response import BrandRetrieveByIsinResponse from ..types.brand_retrieve_by_name_response import BrandRetrieveByNameResponse @@ -2069,65 +2067,6 @@ def retrieve_by_ticker( cast_to=BrandRetrieveByTickerResponse, ) - def retrieve_naics( - self, - *, - input: str, - max_results: int | Omit = omit, - min_results: int | Omit = omit, - timeout_ms: int | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BrandRetrieveNaicsResponse: - """ - Endpoint to classify any brand into a 2022 NAICS code. - - Args: - input: Brand domain or title to retrieve NAICS code for. If a valid domain is provided - in `input`, it will be used for classification, otherwise, we will search for - the brand using the provided title. - - max_results: Maximum number of NAICS codes to return. Must be between 1 and 10. Defaults - to 5. - - min_results: Minimum number of NAICS codes to return. Must be at least 1. Defaults to 1. - - timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer - than this value, it will be aborted with a 408 status code. Maximum allowed - value is 300000ms (5 minutes). - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/brand/naics", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "input": input, - "max_results": max_results, - "min_results": min_results, - "timeout_ms": timeout_ms, - }, - brand_retrieve_naics_params.BrandRetrieveNaicsParams, - ), - ), - cast_to=BrandRetrieveNaicsResponse, - ) - def retrieve_simplified( self, *, @@ -4391,65 +4330,6 @@ async def retrieve_by_ticker( cast_to=BrandRetrieveByTickerResponse, ) - async def retrieve_naics( - self, - *, - input: str, - max_results: int | Omit = omit, - min_results: int | Omit = omit, - timeout_ms: int | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BrandRetrieveNaicsResponse: - """ - Endpoint to classify any brand into a 2022 NAICS code. - - Args: - input: Brand domain or title to retrieve NAICS code for. If a valid domain is provided - in `input`, it will be used for classification, otherwise, we will search for - the brand using the provided title. - - max_results: Maximum number of NAICS codes to return. Must be between 1 and 10. Defaults - to 5. - - min_results: Minimum number of NAICS codes to return. Must be at least 1. Defaults to 1. - - timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer - than this value, it will be aborted with a 408 status code. Maximum allowed - value is 300000ms (5 minutes). - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/brand/naics", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "input": input, - "max_results": max_results, - "min_results": min_results, - "timeout_ms": timeout_ms, - }, - brand_retrieve_naics_params.BrandRetrieveNaicsParams, - ), - ), - cast_to=BrandRetrieveNaicsResponse, - ) - async def retrieve_simplified( self, *, @@ -4741,9 +4621,6 @@ def __init__(self, brand: BrandResource) -> None: self.retrieve_by_ticker = to_raw_response_wrapper( brand.retrieve_by_ticker, ) - self.retrieve_naics = to_raw_response_wrapper( - brand.retrieve_naics, - ) self.retrieve_simplified = to_raw_response_wrapper( brand.retrieve_simplified, ) @@ -4798,9 +4675,6 @@ def __init__(self, brand: AsyncBrandResource) -> None: self.retrieve_by_ticker = async_to_raw_response_wrapper( brand.retrieve_by_ticker, ) - self.retrieve_naics = async_to_raw_response_wrapper( - brand.retrieve_naics, - ) self.retrieve_simplified = async_to_raw_response_wrapper( brand.retrieve_simplified, ) @@ -4855,9 +4729,6 @@ def __init__(self, brand: BrandResource) -> None: self.retrieve_by_ticker = to_streamed_response_wrapper( brand.retrieve_by_ticker, ) - self.retrieve_naics = to_streamed_response_wrapper( - brand.retrieve_naics, - ) self.retrieve_simplified = to_streamed_response_wrapper( brand.retrieve_simplified, ) @@ -4912,9 +4783,6 @@ def __init__(self, brand: AsyncBrandResource) -> None: self.retrieve_by_ticker = async_to_streamed_response_wrapper( brand.retrieve_by_ticker, ) - self.retrieve_naics = async_to_streamed_response_wrapper( - brand.retrieve_naics, - ) self.retrieve_simplified = async_to_streamed_response_wrapper( brand.retrieve_simplified, ) diff --git a/src/brand/dev/types/__init__.py b/src/brand/dev/types/__init__.py index bb12a4f0..8bf4c534 100644 --- a/src/brand/dev/types/__init__.py +++ b/src/brand/dev/types/__init__.py @@ -13,12 +13,10 @@ from .brand_ai_product_response import BrandAIProductResponse as BrandAIProductResponse from .brand_ai_products_response import BrandAIProductsResponse as BrandAIProductsResponse from .brand_web_scrape_md_params import BrandWebScrapeMdParams as BrandWebScrapeMdParams -from .brand_retrieve_naics_params import BrandRetrieveNaicsParams as BrandRetrieveNaicsParams from .brand_web_scrape_html_params import BrandWebScrapeHTMLParams as BrandWebScrapeHTMLParams from .brand_web_scrape_md_response import BrandWebScrapeMdResponse as BrandWebScrapeMdResponse from .brand_retrieve_by_isin_params import BrandRetrieveByIsinParams as BrandRetrieveByIsinParams from .brand_retrieve_by_name_params import BrandRetrieveByNameParams as BrandRetrieveByNameParams -from .brand_retrieve_naics_response import BrandRetrieveNaicsResponse as BrandRetrieveNaicsResponse from .brand_prefetch_by_email_params import BrandPrefetchByEmailParams as BrandPrefetchByEmailParams from .brand_retrieve_by_email_params import BrandRetrieveByEmailParams as BrandRetrieveByEmailParams from .brand_web_scrape_html_response import BrandWebScrapeHTMLResponse as BrandWebScrapeHTMLResponse diff --git a/src/brand/dev/types/brand_retrieve_naics_params.py b/src/brand/dev/types/brand_retrieve_naics_params.py deleted file mode 100644 index 2803c134..00000000 --- a/src/brand/dev/types/brand_retrieve_naics_params.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["BrandRetrieveNaicsParams"] - - -class BrandRetrieveNaicsParams(TypedDict, total=False): - input: Required[str] - """Brand domain or title to retrieve NAICS code for. - - If a valid domain is provided in `input`, it will be used for classification, - otherwise, we will search for the brand using the provided title. - """ - - max_results: Annotated[int, PropertyInfo(alias="maxResults")] - """Maximum number of NAICS codes to return. - - Must be between 1 and 10. Defaults to 5. - """ - - min_results: Annotated[int, PropertyInfo(alias="minResults")] - """Minimum number of NAICS codes to return. Must be at least 1. Defaults to 1.""" - - timeout_ms: Annotated[int, PropertyInfo(alias="timeoutMS")] - """Optional timeout in milliseconds for the request. - - If the request takes longer than this value, it will be aborted with a 408 - status code. Maximum allowed value is 300000ms (5 minutes). - """ diff --git a/src/brand/dev/types/brand_retrieve_naics_response.py b/src/brand/dev/types/brand_retrieve_naics_response.py deleted file mode 100644 index a53da5e4..00000000 --- a/src/brand/dev/types/brand_retrieve_naics_response.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["BrandRetrieveNaicsResponse", "Code"] - - -class Code(BaseModel): - code: str - """NAICS code""" - - confidence: Literal["high", "medium", "low"] - """Confidence level for how well this NAICS code matches the company description""" - - name: str - """NAICS title""" - - -class BrandRetrieveNaicsResponse(BaseModel): - codes: Optional[List[Code]] = None - """Array of NAICS codes and titles.""" - - domain: Optional[str] = None - """Domain found for the brand""" - - status: Optional[str] = None - """Status of the response, e.g., 'ok'""" - - type: Optional[str] = None - """Industry classification type, for naics api it will be `naics`""" diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index 5b76b204..73b5d6d1 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -16,7 +16,6 @@ BrandAIProductResponse, BrandAIProductsResponse, BrandWebScrapeMdResponse, - BrandRetrieveNaicsResponse, BrandWebScrapeHTMLResponse, BrandRetrieveByIsinResponse, BrandRetrieveByNameResponse, @@ -620,51 +619,6 @@ def test_streaming_response_retrieve_by_ticker(self, client: BrandDev) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_method_retrieve_naics(self, client: BrandDev) -> None: - brand = client.brand.retrieve_naics( - input="input", - ) - assert_matches_type(BrandRetrieveNaicsResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_method_retrieve_naics_with_all_params(self, client: BrandDev) -> None: - brand = client.brand.retrieve_naics( - input="input", - max_results=1, - min_results=1, - timeout_ms=1000, - ) - assert_matches_type(BrandRetrieveNaicsResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_raw_response_retrieve_naics(self, client: BrandDev) -> None: - response = client.brand.with_raw_response.retrieve_naics( - input="input", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - brand = response.parse() - assert_matches_type(BrandRetrieveNaicsResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_streaming_response_retrieve_naics(self, client: BrandDev) -> None: - with client.brand.with_streaming_response.retrieve_naics( - input="input", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - brand = response.parse() - assert_matches_type(BrandRetrieveNaicsResponse, brand, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_retrieve_simplified(self, client: BrandDev) -> None: @@ -1466,51 +1420,6 @@ async def test_streaming_response_retrieve_by_ticker(self, async_client: AsyncBr assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_retrieve_naics(self, async_client: AsyncBrandDev) -> None: - brand = await async_client.brand.retrieve_naics( - input="input", - ) - assert_matches_type(BrandRetrieveNaicsResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_retrieve_naics_with_all_params(self, async_client: AsyncBrandDev) -> None: - brand = await async_client.brand.retrieve_naics( - input="input", - max_results=1, - min_results=1, - timeout_ms=1000, - ) - assert_matches_type(BrandRetrieveNaicsResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_raw_response_retrieve_naics(self, async_client: AsyncBrandDev) -> None: - response = await async_client.brand.with_raw_response.retrieve_naics( - input="input", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - brand = await response.parse() - assert_matches_type(BrandRetrieveNaicsResponse, brand, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_streaming_response_retrieve_naics(self, async_client: AsyncBrandDev) -> None: - async with async_client.brand.with_streaming_response.retrieve_naics( - input="input", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - brand = await response.parse() - assert_matches_type(BrandRetrieveNaicsResponse, brand, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_method_retrieve_simplified(self, async_client: AsyncBrandDev) -> None: From 1849fc55c34e35f4c976799fdf828c3ba175db3d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 00:20:21 +0000 Subject: [PATCH 24/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index f73f1bc1..57b9c339 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-071c7e9274f7e2f76732f22559fe8b4b75043a73c2c4af74ad2cc429d4bae357.yml -openapi_spec_hash: e7c47fbf61de6e2e9339f1dbb428cb8e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-13cb1b1b133273b7f4a26ff8772f162417a05342c56bfe26ec427bba0d54c8c3.yml +openapi_spec_hash: 45b57e0ecd3ab113e260ed2329ef5fa2 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From d7e4a181dd0e9146f98bb51bb977300eac439c6e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 06:52:36 +0000 Subject: [PATCH 25/82] perf(client): optimize file structure copying in multipart requests --- src/brand/dev/_files.py | 56 +++++++++++++++++- src/brand/dev/_utils/__init__.py | 1 - src/brand/dev/_utils/_utils.py | 15 ----- tests/test_deepcopy.py | 58 ------------------- tests/test_files.py | 99 +++++++++++++++++++++++++++++++- 5 files changed, 151 insertions(+), 78 deletions(-) delete mode 100644 tests/test_deepcopy.py diff --git a/src/brand/dev/_files.py b/src/brand/dev/_files.py index cc14c14f..0fdce17b 100644 --- a/src/brand/dev/_files.py +++ b/src/brand/dev/_files.py @@ -3,8 +3,8 @@ import io import os import pathlib -from typing import overload -from typing_extensions import TypeGuard +from typing import Sequence, cast, overload +from typing_extensions import TypeVar, TypeGuard import anyio @@ -17,7 +17,9 @@ HttpxFileContent, HttpxRequestFiles, ) -from ._utils import is_tuple_t, is_mapping_t, is_sequence_t +from ._utils import is_list, is_mapping, is_tuple_t, is_mapping_t, is_sequence_t + +_T = TypeVar("_T") def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]: @@ -121,3 +123,51 @@ async def async_read_file_content(file: FileContent) -> HttpxFileContent: return await anyio.Path(file).read_bytes() return file + + +def deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]]) -> _T: + """Copy only the containers along the given paths. + + Used to guard against mutation by extract_files without copying the entire structure. + Only dicts and lists that lie on a path are copied; everything else + is returned by reference. + + For example, given paths=[["foo", "files", "file"]] and the structure: + { + "foo": { + "bar": {"baz": {}}, + "files": {"file": } + } + } + The root dict, "foo", and "files" are copied (they lie on the path). + "bar" and "baz" are returned by reference (off the path). + """ + return _deepcopy_with_paths(item, paths, 0) + + +def _deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]], index: int) -> _T: + if not paths: + return item + if is_mapping(item): + key_to_paths: dict[str, list[Sequence[str]]] = {} + for path in paths: + if index < len(path): + key_to_paths.setdefault(path[index], []).append(path) + + # if no path continues through this mapping, it won't be mutated and copying it is redundant + if not key_to_paths: + return item + + result = dict(item) + for key, subpaths in key_to_paths.items(): + if key in result: + result[key] = _deepcopy_with_paths(result[key], subpaths, index + 1) + return cast(_T, result) + if is_list(item): + array_paths = [path for path in paths if index < len(path) and path[index] == ""] + + # if no path expects a list here, nothing will be mutated inside it - return by reference + if not array_paths: + return cast(_T, item) + return cast(_T, [_deepcopy_with_paths(entry, array_paths, index + 1) for entry in item]) + return item diff --git a/src/brand/dev/_utils/__init__.py b/src/brand/dev/_utils/__init__.py index 10cb66d2..1c090e51 100644 --- a/src/brand/dev/_utils/__init__.py +++ b/src/brand/dev/_utils/__init__.py @@ -24,7 +24,6 @@ coerce_integer as coerce_integer, file_from_path as file_from_path, strip_not_given as strip_not_given, - deepcopy_minimal as deepcopy_minimal, get_async_library as get_async_library, maybe_coerce_float as maybe_coerce_float, get_required_header as get_required_header, diff --git a/src/brand/dev/_utils/_utils.py b/src/brand/dev/_utils/_utils.py index 63b8cd60..771859f5 100644 --- a/src/brand/dev/_utils/_utils.py +++ b/src/brand/dev/_utils/_utils.py @@ -177,21 +177,6 @@ def is_iterable(obj: object) -> TypeGuard[Iterable[object]]: return isinstance(obj, Iterable) -def deepcopy_minimal(item: _T) -> _T: - """Minimal reimplementation of copy.deepcopy() that will only copy certain object types: - - - mappings, e.g. `dict` - - list - - This is done for performance reasons. - """ - if is_mapping(item): - return cast(_T, {k: deepcopy_minimal(v) for k, v in item.items()}) - if is_list(item): - return cast(_T, [deepcopy_minimal(entry) for entry in item]) - return item - - # copied from https://github.com/Rapptz/RoboDanny def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str: size = len(seq) diff --git a/tests/test_deepcopy.py b/tests/test_deepcopy.py deleted file mode 100644 index 937bf726..00000000 --- a/tests/test_deepcopy.py +++ /dev/null @@ -1,58 +0,0 @@ -from brand.dev._utils import deepcopy_minimal - - -def assert_different_identities(obj1: object, obj2: object) -> None: - assert obj1 == obj2 - assert id(obj1) != id(obj2) - - -def test_simple_dict() -> None: - obj1 = {"foo": "bar"} - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - - -def test_nested_dict() -> None: - obj1 = {"foo": {"bar": True}} - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - assert_different_identities(obj1["foo"], obj2["foo"]) - - -def test_complex_nested_dict() -> None: - obj1 = {"foo": {"bar": [{"hello": "world"}]}} - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - assert_different_identities(obj1["foo"], obj2["foo"]) - assert_different_identities(obj1["foo"]["bar"], obj2["foo"]["bar"]) - assert_different_identities(obj1["foo"]["bar"][0], obj2["foo"]["bar"][0]) - - -def test_simple_list() -> None: - obj1 = ["a", "b", "c"] - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - - -def test_nested_list() -> None: - obj1 = ["a", [1, 2, 3]] - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - assert_different_identities(obj1[1], obj2[1]) - - -class MyObject: ... - - -def test_ignores_other_types() -> None: - # custom classes - my_obj = MyObject() - obj1 = {"foo": my_obj} - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - assert obj1["foo"] is my_obj - - # tuples - obj3 = ("a", "b") - obj4 = deepcopy_minimal(obj3) - assert obj3 is obj4 diff --git a/tests/test_files.py b/tests/test_files.py index 9af98b86..e9695db7 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -4,7 +4,8 @@ import pytest from dirty_equals import IsDict, IsList, IsBytes, IsTuple -from brand.dev._files import to_httpx_files, async_to_httpx_files +from brand.dev._files import to_httpx_files, deepcopy_with_paths, async_to_httpx_files +from brand.dev._utils import extract_files readme_path = Path(__file__).parent.parent.joinpath("README.md") @@ -49,3 +50,99 @@ def test_string_not_allowed() -> None: "file": "foo", # type: ignore } ) + + +def assert_different_identities(obj1: object, obj2: object) -> None: + assert obj1 == obj2 + assert obj1 is not obj2 + + +class TestDeepcopyWithPaths: + def test_copies_top_level_dict(self) -> None: + original = {"file": b"data", "other": "value"} + result = deepcopy_with_paths(original, [["file"]]) + assert_different_identities(result, original) + + def test_file_value_is_same_reference(self) -> None: + file_bytes = b"contents" + original = {"file": file_bytes} + result = deepcopy_with_paths(original, [["file"]]) + assert_different_identities(result, original) + assert result["file"] is file_bytes + + def test_list_popped_wholesale(self) -> None: + files = [b"f1", b"f2"] + original = {"files": files, "title": "t"} + result = deepcopy_with_paths(original, [["files", ""]]) + assert_different_identities(result, original) + result_files = result["files"] + assert isinstance(result_files, list) + assert_different_identities(result_files, files) + + def test_nested_array_path_copies_list_and_elements(self) -> None: + elem1 = {"file": b"f1", "extra": 1} + elem2 = {"file": b"f2", "extra": 2} + original = {"items": [elem1, elem2]} + result = deepcopy_with_paths(original, [["items", "", "file"]]) + assert_different_identities(result, original) + result_items = result["items"] + assert isinstance(result_items, list) + assert_different_identities(result_items, original["items"]) + assert_different_identities(result_items[0], elem1) + assert_different_identities(result_items[1], elem2) + + def test_empty_paths_returns_same_object(self) -> None: + original = {"foo": "bar"} + result = deepcopy_with_paths(original, []) + assert result is original + + def test_multiple_paths(self) -> None: + f1 = b"file1" + f2 = b"file2" + original = {"a": f1, "b": f2, "c": "unchanged"} + result = deepcopy_with_paths(original, [["a"], ["b"]]) + assert_different_identities(result, original) + assert result["a"] is f1 + assert result["b"] is f2 + assert result["c"] is original["c"] + + def test_extract_files_does_not_mutate_original_top_level(self) -> None: + file_bytes = b"contents" + original = {"file": file_bytes, "other": "value"} + + copied = deepcopy_with_paths(original, [["file"]]) + extracted = extract_files(copied, paths=[["file"]]) + + assert extracted == [("file", file_bytes)] + assert original == {"file": file_bytes, "other": "value"} + assert copied == {"other": "value"} + + def test_extract_files_does_not_mutate_original_nested_array_path(self) -> None: + file1 = b"f1" + file2 = b"f2" + original = { + "items": [ + {"file": file1, "extra": 1}, + {"file": file2, "extra": 2}, + ], + "title": "example", + } + + copied = deepcopy_with_paths(original, [["items", "", "file"]]) + extracted = extract_files(copied, paths=[["items", "", "file"]]) + + assert extracted == [("items[][file]", file1), ("items[][file]", file2)] + assert original == { + "items": [ + {"file": file1, "extra": 1}, + {"file": file2, "extra": 2}, + ], + "title": "example", + } + assert copied == { + "items": [ + {"extra": 1}, + {"extra": 2}, + ], + "title": "example", + } From 76936a9464660dbe53ad3247c6091e458ea1db20 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 17:36:14 +0000 Subject: [PATCH 26/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 57b9c339..53e55d8b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-13cb1b1b133273b7f4a26ff8772f162417a05342c56bfe26ec427bba0d54c8c3.yml -openapi_spec_hash: 45b57e0ecd3ab113e260ed2329ef5fa2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-356ccbb6bb3c12841b88bd036ef68cc2d5ee48d0bec0ccc4f50479ad4dbb5993.yml +openapi_spec_hash: f9ba30cd05421a6bef83f86d1c96bb82 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 74f22a55a3d82b5adcc63d5f12218a162180ae95 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 18:04:19 +0000 Subject: [PATCH 27/82] feat(api): api update --- .stats.yml | 4 ++-- src/brand/dev/resources/brand.py | 10 ++++++++++ src/brand/dev/types/brand_web_scrape_sitemap_params.py | 6 ++++++ tests/api_resources/test_brand.py | 2 ++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 53e55d8b..0a349a13 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-356ccbb6bb3c12841b88bd036ef68cc2d5ee48d0bec0ccc4f50479ad4dbb5993.yml -openapi_spec_hash: f9ba30cd05421a6bef83f86d1c96bb82 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-b6262c6c346b4e4c6db18bc0c74585bb0b1d8f239048e893e76de2fcfd15ce01.yml +openapi_spec_hash: 157593d2318122199aedd86d7755f941 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index cb18c074..9521ffd9 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -2276,6 +2276,7 @@ def web_scrape_sitemap( *, domain: str, max_links: int | Omit = omit, + url_regex: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2292,6 +2293,9 @@ def web_scrape_sitemap( max_links: Maximum number of links to return from the sitemap crawl. Defaults to 10,000. Minimum is 1, maximum is 100,000. + url_regex: Optional RE2-compatible regex pattern. Only URLs matching this pattern are + returned and counted against maxLinks. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2311,6 +2315,7 @@ def web_scrape_sitemap( { "domain": domain, "max_links": max_links, + "url_regex": url_regex, }, brand_web_scrape_sitemap_params.BrandWebScrapeSitemapParams, ), @@ -4541,6 +4546,7 @@ async def web_scrape_sitemap( *, domain: str, max_links: int | Omit = omit, + url_regex: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -4557,6 +4563,9 @@ async def web_scrape_sitemap( max_links: Maximum number of links to return from the sitemap crawl. Defaults to 10,000. Minimum is 1, maximum is 100,000. + url_regex: Optional RE2-compatible regex pattern. Only URLs matching this pattern are + returned and counted against maxLinks. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -4576,6 +4585,7 @@ async def web_scrape_sitemap( { "domain": domain, "max_links": max_links, + "url_regex": url_regex, }, brand_web_scrape_sitemap_params.BrandWebScrapeSitemapParams, ), diff --git a/src/brand/dev/types/brand_web_scrape_sitemap_params.py b/src/brand/dev/types/brand_web_scrape_sitemap_params.py index 8d556085..0a3be070 100644 --- a/src/brand/dev/types/brand_web_scrape_sitemap_params.py +++ b/src/brand/dev/types/brand_web_scrape_sitemap_params.py @@ -18,3 +18,9 @@ class BrandWebScrapeSitemapParams(TypedDict, total=False): Defaults to 10,000. Minimum is 1, maximum is 100,000. """ + + url_regex: Annotated[str, PropertyInfo(alias="urlRegex")] + """Optional RE2-compatible regex pattern. + + Only URLs matching this pattern are returned and counted against maxLinks. + """ diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index 73b5d6d1..29a4599c 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -800,6 +800,7 @@ def test_method_web_scrape_sitemap_with_all_params(self, client: BrandDev) -> No brand = client.brand.web_scrape_sitemap( domain="domain", max_links=1, + url_regex="^https?://[^/]+/blog/", ) assert_matches_type(BrandWebScrapeSitemapResponse, brand, path=["response"]) @@ -1601,6 +1602,7 @@ async def test_method_web_scrape_sitemap_with_all_params(self, async_client: Asy brand = await async_client.brand.web_scrape_sitemap( domain="domain", max_links=1, + url_regex="^https?://[^/]+/blog/", ) assert_matches_type(BrandWebScrapeSitemapResponse, brand, path=["response"]) From cd71a25276e2ecbe2fc5eee21263d6099ec413a3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 13:44:10 +0000 Subject: [PATCH 28/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0a349a13..069181b4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-b6262c6c346b4e4c6db18bc0c74585bb0b1d8f239048e893e76de2fcfd15ce01.yml -openapi_spec_hash: 157593d2318122199aedd86d7755f941 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-9515e3e36a3c0be3fdd397d0e2934cf9c2accbe74b04711dcccb107ced266329.yml +openapi_spec_hash: 9409c01546e85f69aba881fb1d34c061 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 28007d91dbe45b20fc33670466006d9934ac7c5a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 03:39:16 +0000 Subject: [PATCH 29/82] chore(internal): more robust bootstrap script --- scripts/bootstrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/bootstrap b/scripts/bootstrap index b430fee3..fe8451e4 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,7 +4,7 @@ set -e cd "$(dirname "$0")/.." -if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "${SKIP_BREW:-}" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { echo -n "==> Install Homebrew dependencies? (y/N): " read -r response From 5c2e885a86a4c77746d10c7bac1892d1067cc599 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:55:11 +0000 Subject: [PATCH 30/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 069181b4..2008d73b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-9515e3e36a3c0be3fdd397d0e2934cf9c2accbe74b04711dcccb107ced266329.yml -openapi_spec_hash: 9409c01546e85f69aba881fb1d34c061 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-bad99401f1babc6e34ab94e1700768545292825447e43fecbbfe426ae89c594c.yml +openapi_spec_hash: 2b40ce701c90ae2cb9f89ee7b52bbf5b config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 390e85cad5c6199e1a3d03de72e77d54f0108234 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 01:01:26 +0000 Subject: [PATCH 31/82] feat(api): api update --- .stats.yml | 4 ++-- src/brand/dev/resources/brand.py | 24 +++++++++++++++++++ .../dev/types/brand_web_scrape_html_params.py | 7 ++++++ .../dev/types/brand_web_scrape_md_params.py | 7 ++++++ tests/api_resources/test_brand.py | 4 ++++ 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 2008d73b..21683637 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-bad99401f1babc6e34ab94e1700768545292825447e43fecbbfe426ae89c594c.yml -openapi_spec_hash: 2b40ce701c90ae2cb9f89ee7b52bbf5b +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-5feeb635e8e8c4d52f5ab3e689888134427dae866a0c5264a31598a538673130.yml +openapi_spec_hash: 9619b7626bbad5468f41623726e229c9 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index 9521ffd9..3a1ba965 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -2122,6 +2122,7 @@ def web_scrape_html( *, url: str, max_age_ms: int | Omit = omit, + parse_pdf: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2139,6 +2140,10 @@ def web_scrape_html( younger than this many milliseconds. Defaults to 1 day (86400000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + parse_pdf: When true (default), PDF URLs are fetched and their text layer is extracted and + returned wrapped in . When false, PDF URLs are skipped + and a 400 WEBSITE_ACCESS_ERROR is returned. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2158,6 +2163,7 @@ def web_scrape_html( { "url": url, "max_age_ms": max_age_ms, + "parse_pdf": parse_pdf, }, brand_web_scrape_html_params.BrandWebScrapeHTMLParams, ), @@ -2212,6 +2218,7 @@ def web_scrape_md( include_images: bool | Omit = omit, include_links: bool | Omit = omit, max_age_ms: int | Omit = omit, + parse_pdf: bool | Omit = omit, shorten_base64_images: bool | Omit = omit, use_main_content_only: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -2236,6 +2243,10 @@ def web_scrape_md( younger than this many milliseconds. Defaults to 1 day (86400000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + parse_pdf: When true (default), PDF URLs are fetched and their text layer is extracted and + converted to Markdown. When false, PDF URLs are skipped and a 400 + WEBSITE_ACCESS_ERROR is returned. + shorten_base64_images: Shorten base64-encoded image data in the Markdown output use_main_content_only: Extract only the main content of the page, excluding headers, footers, sidebars, @@ -2262,6 +2273,7 @@ def web_scrape_md( "include_images": include_images, "include_links": include_links, "max_age_ms": max_age_ms, + "parse_pdf": parse_pdf, "shorten_base64_images": shorten_base64_images, "use_main_content_only": use_main_content_only, }, @@ -4390,6 +4402,7 @@ async def web_scrape_html( *, url: str, max_age_ms: int | Omit = omit, + parse_pdf: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -4407,6 +4420,10 @@ async def web_scrape_html( younger than this many milliseconds. Defaults to 1 day (86400000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + parse_pdf: When true (default), PDF URLs are fetched and their text layer is extracted and + returned wrapped in . When false, PDF URLs are skipped + and a 400 WEBSITE_ACCESS_ERROR is returned. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -4426,6 +4443,7 @@ async def web_scrape_html( { "url": url, "max_age_ms": max_age_ms, + "parse_pdf": parse_pdf, }, brand_web_scrape_html_params.BrandWebScrapeHTMLParams, ), @@ -4482,6 +4500,7 @@ async def web_scrape_md( include_images: bool | Omit = omit, include_links: bool | Omit = omit, max_age_ms: int | Omit = omit, + parse_pdf: bool | Omit = omit, shorten_base64_images: bool | Omit = omit, use_main_content_only: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -4506,6 +4525,10 @@ async def web_scrape_md( younger than this many milliseconds. Defaults to 1 day (86400000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + parse_pdf: When true (default), PDF URLs are fetched and their text layer is extracted and + converted to Markdown. When false, PDF URLs are skipped and a 400 + WEBSITE_ACCESS_ERROR is returned. + shorten_base64_images: Shorten base64-encoded image data in the Markdown output use_main_content_only: Extract only the main content of the page, excluding headers, footers, sidebars, @@ -4532,6 +4555,7 @@ async def web_scrape_md( "include_images": include_images, "include_links": include_links, "max_age_ms": max_age_ms, + "parse_pdf": parse_pdf, "shorten_base64_images": shorten_base64_images, "use_main_content_only": use_main_content_only, }, diff --git a/src/brand/dev/types/brand_web_scrape_html_params.py b/src/brand/dev/types/brand_web_scrape_html_params.py index ea7da75d..0cdbae15 100644 --- a/src/brand/dev/types/brand_web_scrape_html_params.py +++ b/src/brand/dev/types/brand_web_scrape_html_params.py @@ -19,3 +19,10 @@ class BrandWebScrapeHTMLParams(TypedDict, total=False): younger than this many milliseconds. Defaults to 1 day (86400000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. """ + + parse_pdf: Annotated[bool, PropertyInfo(alias="parsePDF")] + """ + When true (default), PDF URLs are fetched and their text layer is extracted and + returned wrapped in . When false, PDF URLs are skipped + and a 400 WEBSITE_ACCESS_ERROR is returned. + """ diff --git a/src/brand/dev/types/brand_web_scrape_md_params.py b/src/brand/dev/types/brand_web_scrape_md_params.py index e3964edd..c0dbeadd 100644 --- a/src/brand/dev/types/brand_web_scrape_md_params.py +++ b/src/brand/dev/types/brand_web_scrape_md_params.py @@ -29,6 +29,13 @@ class BrandWebScrapeMdParams(TypedDict, total=False): omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. """ + parse_pdf: Annotated[bool, PropertyInfo(alias="parsePDF")] + """ + When true (default), PDF URLs are fetched and their text layer is extracted and + converted to Markdown. When false, PDF URLs are skipped and a 400 + WEBSITE_ACCESS_ERROR is returned. + """ + shorten_base64_images: Annotated[bool, PropertyInfo(alias="shortenBase64Images")] """Shorten base64-encoded image data in the Markdown output""" diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index 29a4599c..a776c5a7 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -676,6 +676,7 @@ def test_method_web_scrape_html_with_all_params(self, client: BrandDev) -> None: brand = client.brand.web_scrape_html( url="https://example.com", max_age_ms=0, + parse_pdf=True, ) assert_matches_type(BrandWebScrapeHTMLResponse, brand, path=["response"]) @@ -755,6 +756,7 @@ def test_method_web_scrape_md_with_all_params(self, client: BrandDev) -> None: include_images=True, include_links=True, max_age_ms=0, + parse_pdf=True, shorten_base64_images=True, use_main_content_only=True, ) @@ -1478,6 +1480,7 @@ async def test_method_web_scrape_html_with_all_params(self, async_client: AsyncB brand = await async_client.brand.web_scrape_html( url="https://example.com", max_age_ms=0, + parse_pdf=True, ) assert_matches_type(BrandWebScrapeHTMLResponse, brand, path=["response"]) @@ -1557,6 +1560,7 @@ async def test_method_web_scrape_md_with_all_params(self, async_client: AsyncBra include_images=True, include_links=True, max_age_ms=0, + parse_pdf=True, shorten_base64_images=True, use_main_content_only=True, ) From f9047c77841d0b1707cdeab18880d93dd2e6a115 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 01:55:49 +0000 Subject: [PATCH 32/82] feat(api): api update --- .stats.yml | 4 ++-- src/brand/dev/types/brand_ai_product_response.py | 8 ++++++++ src/brand/dev/types/brand_ai_products_response.py | 8 ++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 21683637..c2c1aed7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-5feeb635e8e8c4d52f5ab3e689888134427dae866a0c5264a31598a538673130.yml -openapi_spec_hash: 9619b7626bbad5468f41623726e229c9 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-a79be9698f9c8908538102f0ea3ed43ce85be94c723f81ce3f7143b6dd394c62.yml +openapi_spec_hash: 840d5e6c9fd1710942bd61ab08f7f411 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/types/brand_ai_product_response.py b/src/brand/dev/types/brand_ai_product_response.py index eb31de27..e26edb2a 100644 --- a/src/brand/dev/types/brand_ai_product_response.py +++ b/src/brand/dev/types/brand_ai_product_response.py @@ -23,6 +23,14 @@ class Product(BaseModel): name: str """Name of the product""" + sku: Optional[str] = None + """Stock Keeping Unit (product identifier). + + Extracted from structured data (JSON-LD Product.sku), microdata, meta tags, + platform-specific identifiers (e.g. Amazon ASIN, Etsy listing ID), or visible + SKU/Model/Item # text. Null if no identifier is found. + """ + tags: List[str] """Tags associated with the product""" diff --git a/src/brand/dev/types/brand_ai_products_response.py b/src/brand/dev/types/brand_ai_products_response.py index 4100a1a9..f0dce8c1 100644 --- a/src/brand/dev/types/brand_ai_products_response.py +++ b/src/brand/dev/types/brand_ai_products_response.py @@ -21,6 +21,14 @@ class Product(BaseModel): name: str """Name of the product""" + sku: Optional[str] = None + """Stock Keeping Unit (product identifier). + + Extracted from structured data (JSON-LD Product.sku), microdata, meta tags, + platform-specific identifiers (e.g. Amazon ASIN, Etsy listing ID), or visible + SKU/Model/Item # text. Null if no identifier is found. + """ + tags: List[str] """Tags associated with the product""" From 3e783d9ad471999f34ed2cbc92a58099f15ce9cb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 02:02:46 +0000 Subject: [PATCH 33/82] feat(api): api update --- .stats.yml | 4 ++-- src/brand/dev/types/brand_ai_product_response.py | 7 +------ src/brand/dev/types/brand_ai_products_response.py | 7 +------ 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/.stats.yml b/.stats.yml index c2c1aed7..5d1e9ce3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-a79be9698f9c8908538102f0ea3ed43ce85be94c723f81ce3f7143b6dd394c62.yml -openapi_spec_hash: 840d5e6c9fd1710942bd61ab08f7f411 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-2952aaffb14241bbdb6df2c3f4a83d21527a969cc1ed03f0ee4b3399973891a5.yml +openapi_spec_hash: ea489ac04d293ed777fefb705782e165 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/types/brand_ai_product_response.py b/src/brand/dev/types/brand_ai_product_response.py index e26edb2a..1880ca56 100644 --- a/src/brand/dev/types/brand_ai_product_response.py +++ b/src/brand/dev/types/brand_ai_product_response.py @@ -24,12 +24,7 @@ class Product(BaseModel): """Name of the product""" sku: Optional[str] = None - """Stock Keeping Unit (product identifier). - - Extracted from structured data (JSON-LD Product.sku), microdata, meta tags, - platform-specific identifiers (e.g. Amazon ASIN, Etsy listing ID), or visible - SKU/Model/Item # text. Null if no identifier is found. - """ + """Stock Keeping Unit (product identifier). Null if no identifier is found.""" tags: List[str] """Tags associated with the product""" diff --git a/src/brand/dev/types/brand_ai_products_response.py b/src/brand/dev/types/brand_ai_products_response.py index f0dce8c1..a0747951 100644 --- a/src/brand/dev/types/brand_ai_products_response.py +++ b/src/brand/dev/types/brand_ai_products_response.py @@ -22,12 +22,7 @@ class Product(BaseModel): """Name of the product""" sku: Optional[str] = None - """Stock Keeping Unit (product identifier). - - Extracted from structured data (JSON-LD Product.sku), microdata, meta tags, - platform-specific identifiers (e.g. Amazon ASIN, Etsy listing ID), or visible - SKU/Model/Item # text. Null if no identifier is found. - """ + """Stock Keeping Unit (product identifier). Null if no identifier is found.""" tags: List[str] """Tags associated with the product""" From f0e8b85c8ea61f9a8cf4c61f2a93fd68930a95ed Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 25 Apr 2026 19:33:50 +0000 Subject: [PATCH 34/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 5d1e9ce3..31233eaa 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-2952aaffb14241bbdb6df2c3f4a83d21527a969cc1ed03f0ee4b3399973891a5.yml -openapi_spec_hash: ea489ac04d293ed777fefb705782e165 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-0556b1d48d8bff03ed38b606fdb77620a74cc8e0df9dadd3445d20a0662cea5d.yml +openapi_spec_hash: 953bc57b3ad20e8dba19cecf23ef0425 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 9ac7adc4a943b0895882ded29fbee22a1b830f38 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 25 Apr 2026 23:41:20 +0000 Subject: [PATCH 35/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 31233eaa..e319589f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-0556b1d48d8bff03ed38b606fdb77620a74cc8e0df9dadd3445d20a0662cea5d.yml -openapi_spec_hash: 953bc57b3ad20e8dba19cecf23ef0425 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-83beb1a8b5d19a48d62b81a81c14badd01dab69a3b636d63dc70199de3644d0d.yml +openapi_spec_hash: eeb3bbf7ccb4d14e0288cc88045dd007 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From e4f1307a639a0c14467bd4c6af34eafe96b86033 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 02:24:32 +0000 Subject: [PATCH 36/82] feat(api): api update --- .stats.yml | 4 +-- src/brand/dev/resources/brand.py | 58 +++++++++++++------------------- 2 files changed, 26 insertions(+), 36 deletions(-) diff --git a/.stats.yml b/.stats.yml index e319589f..93997187 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-83beb1a8b5d19a48d62b81a81c14badd01dab69a3b636d63dc70199de3644d0d.yml -openapi_spec_hash: eeb3bbf7ccb4d14e0288cc88045dd007 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-2c2882c6fc2b0cd3a9cc19fd1a01f969b9333a96f8bc53f83b0c3e051ddc97c4.yml +openapi_spec_hash: af01b87a25eeafec960c95f31acab235 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index 3a1ba965..01892f8e 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -271,9 +271,8 @@ def ai_product( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandAIProductResponse: """ - Beta feature: Given a single URL, determines if it is a product detail page, - classifies the platform/product type, and extracts the product information. - Supports Amazon, TikTok Shop, Etsy, and generic ecommerce sites. + Given a single URL, determines if it is a product page and extracts the product + information. Args: url: The product page URL to extract product data from. @@ -318,11 +317,11 @@ def ai_products( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandAIProductsResponse: - """Beta feature: Extract product information from a brand's website. + """Extract product information from a brand's website. - We will - analyze the website and return a list of products with details such as name, - description, image, pricing, features, and more. + We will analyze the website + and return a list of products with details such as name, description, image, + pricing, features, and more. Args: domain: The domain name to analyze. @@ -356,11 +355,11 @@ def ai_products( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandAIProductsResponse: - """Beta feature: Extract product information from a brand's website. + """Extract product information from a brand's website. - We will - analyze the website and return a list of products with details such as name, - description, image, pricing, features, and more. + We will analyze the website + and return a list of products with details such as name, description, image, + pricing, features, and more. Args: direct_url: A specific URL to use directly as the starting point for extraction without @@ -928,9 +927,7 @@ def prefetch( ) -> BrandPrefetchResponse: """ Signal that you may fetch brand data for a particular domain soon to improve - latency. This endpoint does not charge credits and is available for paid - customers to optimize future requests. [You must be on a paid plan to use this - endpoint] + latency. Args: domain: Domain name to prefetch brand data for @@ -978,9 +975,7 @@ def prefetch_by_email( Signal that you may fetch brand data for a particular domain soon to improve latency. This endpoint accepts an email address, extracts the domain from it, validates that it's not a disposable or free email provider, and queues the - domain for prefetching. This endpoint does not charge credits and is available - for paid customers to optimize future requests. [You must be on a paid plan to - use this endpoint] + domain for prefetching. Args: email: Email address to prefetch brand data for. The domain will be extracted from the @@ -2551,9 +2546,8 @@ async def ai_product( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandAIProductResponse: """ - Beta feature: Given a single URL, determines if it is a product detail page, - classifies the platform/product type, and extracts the product information. - Supports Amazon, TikTok Shop, Etsy, and generic ecommerce sites. + Given a single URL, determines if it is a product page and extracts the product + information. Args: url: The product page URL to extract product data from. @@ -2598,11 +2592,11 @@ async def ai_products( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandAIProductsResponse: - """Beta feature: Extract product information from a brand's website. + """Extract product information from a brand's website. - We will - analyze the website and return a list of products with details such as name, - description, image, pricing, features, and more. + We will analyze the website + and return a list of products with details such as name, description, image, + pricing, features, and more. Args: domain: The domain name to analyze. @@ -2636,11 +2630,11 @@ async def ai_products( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandAIProductsResponse: - """Beta feature: Extract product information from a brand's website. + """Extract product information from a brand's website. - We will - analyze the website and return a list of products with details such as name, - description, image, pricing, features, and more. + We will analyze the website + and return a list of products with details such as name, description, image, + pricing, features, and more. Args: direct_url: A specific URL to use directly as the starting point for extraction without @@ -3208,9 +3202,7 @@ async def prefetch( ) -> BrandPrefetchResponse: """ Signal that you may fetch brand data for a particular domain soon to improve - latency. This endpoint does not charge credits and is available for paid - customers to optimize future requests. [You must be on a paid plan to use this - endpoint] + latency. Args: domain: Domain name to prefetch brand data for @@ -3258,9 +3250,7 @@ async def prefetch_by_email( Signal that you may fetch brand data for a particular domain soon to improve latency. This endpoint accepts an email address, extracts the domain from it, validates that it's not a disposable or free email provider, and queues the - domain for prefetching. This endpoint does not charge credits and is available - for paid customers to optimize future requests. [You must be on a paid plan to - use this endpoint] + domain for prefetching. Args: email: Email address to prefetch brand data for. The domain will be extracted from the From 37cd5ca3b3dc1795155414ca40c667d5cf1c27f7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 19:24:12 +0000 Subject: [PATCH 37/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 93997187..f6a695dd 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-2c2882c6fc2b0cd3a9cc19fd1a01f969b9333a96f8bc53f83b0c3e051ddc97c4.yml -openapi_spec_hash: af01b87a25eeafec960c95f31acab235 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-e80ff2bd2c88a023bfab185593d9bbc03d81b79b46b178a724b67ffffa504fc8.yml +openapi_spec_hash: fc81cc80368feba40e621e9449f03c6a config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 5573ee7e42f5018a95e85e66460bfccc3d695a2a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 23:25:28 +0000 Subject: [PATCH 38/82] feat(api): api update --- .stats.yml | 4 +-- src/brand/dev/resources/brand.py | 36 +++++++++++++++++++ .../dev/types/brand_ai_product_params.py | 7 ++++ .../dev/types/brand_ai_products_params.py | 14 ++++++++ tests/api_resources/test_brand.py | 6 ++++ 5 files changed, 65 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index f6a695dd..c5912706 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-e80ff2bd2c88a023bfab185593d9bbc03d81b79b46b178a724b67ffffa504fc8.yml -openapi_spec_hash: fc81cc80368feba40e621e9449f03c6a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-632b293af91698403da1bcaf3fb91259636c772e5071a2dd0fc63c42cb591307.yml +openapi_spec_hash: d03dc81f33ef92c74aa3b5dea35cdeae config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index 01892f8e..292b331c 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -262,6 +262,7 @@ def ai_product( self, *, url: str, + max_age_ms: int | Omit = omit, timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -277,6 +278,10 @@ def ai_product( Args: url: The product page URL to extract product data from. + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 7 days (604800000 ms) when + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + timeout_ms: Optional timeout in milliseconds for the request. Maximum allowed value is 300000ms (5 minutes). @@ -293,6 +298,7 @@ def ai_product( body=maybe_transform( { "url": url, + "max_age_ms": max_age_ms, "timeout_ms": timeout_ms, }, brand_ai_product_params.BrandAIProductParams, @@ -308,6 +314,7 @@ def ai_products( self, *, domain: str, + max_age_ms: int | Omit = omit, max_products: int | Omit = omit, timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -326,6 +333,10 @@ def ai_products( Args: domain: The domain name to analyze. + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 7 days (604800000 ms) when + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + max_products: Maximum number of products to extract. timeout_ms: Optional timeout in milliseconds for the request. Maximum allowed value is @@ -346,6 +357,7 @@ def ai_products( self, *, direct_url: str, + max_age_ms: int | Omit = omit, max_products: int | Omit = omit, timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -365,6 +377,10 @@ def ai_products( direct_url: A specific URL to use directly as the starting point for extraction without domain resolution. + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 7 days (604800000 ms) when + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + max_products: Maximum number of products to extract. timeout_ms: Optional timeout in milliseconds for the request. Maximum allowed value is @@ -385,6 +401,7 @@ def ai_products( self, *, domain: str | Omit = omit, + max_age_ms: int | Omit = omit, max_products: int | Omit = omit, timeout_ms: int | Omit = omit, direct_url: str | Omit = omit, @@ -400,6 +417,7 @@ def ai_products( body=maybe_transform( { "domain": domain, + "max_age_ms": max_age_ms, "max_products": max_products, "timeout_ms": timeout_ms, "direct_url": direct_url, @@ -2537,6 +2555,7 @@ async def ai_product( self, *, url: str, + max_age_ms: int | Omit = omit, timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -2552,6 +2571,10 @@ async def ai_product( Args: url: The product page URL to extract product data from. + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 7 days (604800000 ms) when + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + timeout_ms: Optional timeout in milliseconds for the request. Maximum allowed value is 300000ms (5 minutes). @@ -2568,6 +2591,7 @@ async def ai_product( body=await async_maybe_transform( { "url": url, + "max_age_ms": max_age_ms, "timeout_ms": timeout_ms, }, brand_ai_product_params.BrandAIProductParams, @@ -2583,6 +2607,7 @@ async def ai_products( self, *, domain: str, + max_age_ms: int | Omit = omit, max_products: int | Omit = omit, timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -2601,6 +2626,10 @@ async def ai_products( Args: domain: The domain name to analyze. + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 7 days (604800000 ms) when + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + max_products: Maximum number of products to extract. timeout_ms: Optional timeout in milliseconds for the request. Maximum allowed value is @@ -2621,6 +2650,7 @@ async def ai_products( self, *, direct_url: str, + max_age_ms: int | Omit = omit, max_products: int | Omit = omit, timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -2640,6 +2670,10 @@ async def ai_products( direct_url: A specific URL to use directly as the starting point for extraction without domain resolution. + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 7 days (604800000 ms) when + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + max_products: Maximum number of products to extract. timeout_ms: Optional timeout in milliseconds for the request. Maximum allowed value is @@ -2660,6 +2694,7 @@ async def ai_products( self, *, domain: str | Omit = omit, + max_age_ms: int | Omit = omit, max_products: int | Omit = omit, timeout_ms: int | Omit = omit, direct_url: str | Omit = omit, @@ -2675,6 +2710,7 @@ async def ai_products( body=await async_maybe_transform( { "domain": domain, + "max_age_ms": max_age_ms, "max_products": max_products, "timeout_ms": timeout_ms, "direct_url": direct_url, diff --git a/src/brand/dev/types/brand_ai_product_params.py b/src/brand/dev/types/brand_ai_product_params.py index 17e62c90..bc65ee6c 100644 --- a/src/brand/dev/types/brand_ai_product_params.py +++ b/src/brand/dev/types/brand_ai_product_params.py @@ -13,6 +13,13 @@ class BrandAIProductParams(TypedDict, total=False): url: Required[str] """The product page URL to extract product data from.""" + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] + """ + Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 7 days (604800000 ms) when + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + """ + timeout_ms: Annotated[int, PropertyInfo(alias="timeoutMS")] """Optional timeout in milliseconds for the request. diff --git a/src/brand/dev/types/brand_ai_products_params.py b/src/brand/dev/types/brand_ai_products_params.py index 9a61efee..978a6052 100644 --- a/src/brand/dev/types/brand_ai_products_params.py +++ b/src/brand/dev/types/brand_ai_products_params.py @@ -14,6 +14,13 @@ class ByDomain(TypedDict, total=False): domain: Required[str] """The domain name to analyze.""" + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] + """ + Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 7 days (604800000 ms) when + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + """ + max_products: Annotated[int, PropertyInfo(alias="maxProducts")] """Maximum number of products to extract.""" @@ -31,6 +38,13 @@ class ByDirectURL(TypedDict, total=False): domain resolution. """ + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] + """ + Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 7 days (604800000 ms) when + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. + """ + max_products: Annotated[int, PropertyInfo(alias="maxProducts")] """Maximum number of products to extract.""" diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index a776c5a7..da9b355e 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -92,6 +92,7 @@ def test_method_ai_product(self, client: BrandDev) -> None: def test_method_ai_product_with_all_params(self, client: BrandDev) -> None: brand = client.brand.ai_product( url="https://example.com", + max_age_ms=0, timeout_ms=1000, ) assert_matches_type(BrandAIProductResponse, brand, path=["response"]) @@ -135,6 +136,7 @@ def test_method_ai_products_overload_1(self, client: BrandDev) -> None: def test_method_ai_products_with_all_params_overload_1(self, client: BrandDev) -> None: brand = client.brand.ai_products( domain="domain", + max_age_ms=0, max_products=1, timeout_ms=1000, ) @@ -179,6 +181,7 @@ def test_method_ai_products_overload_2(self, client: BrandDev) -> None: def test_method_ai_products_with_all_params_overload_2(self, client: BrandDev) -> None: brand = client.brand.ai_products( direct_url="https://example.com", + max_age_ms=0, max_products=1, timeout_ms=1000, ) @@ -896,6 +899,7 @@ async def test_method_ai_product(self, async_client: AsyncBrandDev) -> None: async def test_method_ai_product_with_all_params(self, async_client: AsyncBrandDev) -> None: brand = await async_client.brand.ai_product( url="https://example.com", + max_age_ms=0, timeout_ms=1000, ) assert_matches_type(BrandAIProductResponse, brand, path=["response"]) @@ -939,6 +943,7 @@ async def test_method_ai_products_overload_1(self, async_client: AsyncBrandDev) async def test_method_ai_products_with_all_params_overload_1(self, async_client: AsyncBrandDev) -> None: brand = await async_client.brand.ai_products( domain="domain", + max_age_ms=0, max_products=1, timeout_ms=1000, ) @@ -983,6 +988,7 @@ async def test_method_ai_products_overload_2(self, async_client: AsyncBrandDev) async def test_method_ai_products_with_all_params_overload_2(self, async_client: AsyncBrandDev) -> None: brand = await async_client.brand.ai_products( direct_url="https://example.com", + max_age_ms=0, max_products=1, timeout_ms=1000, ) From c74c3699de55fd58f298fa9a9d378db7f677a344 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 03:37:18 +0000 Subject: [PATCH 39/82] fix: use correct field name format for multipart file arrays --- src/brand/dev/_qs.py | 8 ++----- src/brand/dev/_types.py | 3 +++ src/brand/dev/_utils/_utils.py | 42 +++++++++++++++++++++++++++------- tests/test_extract_files.py | 28 +++++++++++++++++++---- tests/test_files.py | 2 +- 5 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/brand/dev/_qs.py b/src/brand/dev/_qs.py index de8c99bc..4127c19c 100644 --- a/src/brand/dev/_qs.py +++ b/src/brand/dev/_qs.py @@ -2,17 +2,13 @@ from typing import Any, List, Tuple, Union, Mapping, TypeVar from urllib.parse import parse_qs, urlencode -from typing_extensions import Literal, get_args +from typing_extensions import get_args -from ._types import NotGiven, not_given +from ._types import NotGiven, ArrayFormat, NestedFormat, not_given from ._utils import flatten _T = TypeVar("_T") - -ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] -NestedFormat = Literal["dots", "brackets"] - PrimitiveData = Union[str, int, float, bool, None] # this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"] # https://github.com/microsoft/pyright/issues/3555 diff --git a/src/brand/dev/_types.py b/src/brand/dev/_types.py index fce564e0..eff6aae2 100644 --- a/src/brand/dev/_types.py +++ b/src/brand/dev/_types.py @@ -47,6 +47,9 @@ ModelT = TypeVar("ModelT", bound=pydantic.BaseModel) _T = TypeVar("_T") +ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] +NestedFormat = Literal["dots", "brackets"] + # Approximates httpx internal ProxiesTypes and RequestFiles types # while adding support for `PathLike` instances diff --git a/src/brand/dev/_utils/_utils.py b/src/brand/dev/_utils/_utils.py index 771859f5..199cd231 100644 --- a/src/brand/dev/_utils/_utils.py +++ b/src/brand/dev/_utils/_utils.py @@ -17,11 +17,11 @@ ) from pathlib import Path from datetime import date, datetime -from typing_extensions import TypeGuard +from typing_extensions import TypeGuard, get_args import sniffio -from .._types import Omit, NotGiven, FileTypes, HeadersLike +from .._types import Omit, NotGiven, FileTypes, ArrayFormat, HeadersLike _T = TypeVar("_T") _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) @@ -40,25 +40,45 @@ def extract_files( query: Mapping[str, object], *, paths: Sequence[Sequence[str]], + array_format: ArrayFormat = "brackets", ) -> list[tuple[str, FileTypes]]: """Recursively extract files from the given dictionary based on specified paths. A path may look like this ['foo', 'files', '', 'data']. + ``array_format`` controls how ```` segments contribute to the emitted + field name. Supported values: ``"brackets"`` (``foo[]``), ``"repeat"`` and + ``"comma"`` (``foo``), ``"indices"`` (``foo[0]``, ``foo[1]``). + Note: this mutates the given dictionary. """ files: list[tuple[str, FileTypes]] = [] for path in paths: - files.extend(_extract_items(query, path, index=0, flattened_key=None)) + files.extend(_extract_items(query, path, index=0, flattened_key=None, array_format=array_format)) return files +def _array_suffix(array_format: ArrayFormat, array_index: int) -> str: + if array_format == "brackets": + return "[]" + if array_format == "indices": + return f"[{array_index}]" + if array_format == "repeat" or array_format == "comma": + # Both repeat the bare field name for each file part; there is no + # meaningful way to comma-join binary parts. + return "" + raise NotImplementedError( + f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" + ) + + def _extract_items( obj: object, path: Sequence[str], *, index: int, flattened_key: str | None, + array_format: ArrayFormat, ) -> list[tuple[str, FileTypes]]: try: key = path[index] @@ -75,9 +95,11 @@ def _extract_items( if is_list(obj): files: list[tuple[str, FileTypes]] = [] - for entry in obj: - assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "") - files.append((flattened_key + "[]", cast(FileTypes, entry))) + for array_index, entry in enumerate(obj): + suffix = _array_suffix(array_format, array_index) + emitted_key = (flattened_key + suffix) if flattened_key else suffix + assert_is_file_content(entry, key=emitted_key) + files.append((emitted_key, cast(FileTypes, entry))) return files assert_is_file_content(obj, key=flattened_key) @@ -106,6 +128,7 @@ def _extract_items( path, index=index, flattened_key=flattened_key, + array_format=array_format, ) elif is_list(obj): if key != "": @@ -117,9 +140,12 @@ def _extract_items( item, path, index=index, - flattened_key=flattened_key + "[]" if flattened_key is not None else "[]", + flattened_key=( + (flattened_key if flattened_key is not None else "") + _array_suffix(array_format, array_index) + ), + array_format=array_format, ) - for item in obj + for array_index, item in enumerate(obj) ] ) diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py index af7ecd37..ba150ec3 100644 --- a/tests/test_extract_files.py +++ b/tests/test_extract_files.py @@ -4,7 +4,7 @@ import pytest -from brand.dev._types import FileTypes +from brand.dev._types import FileTypes, ArrayFormat from brand.dev._utils import extract_files @@ -37,10 +37,7 @@ def test_multiple_files() -> None: def test_top_level_file_array() -> None: query = {"files": [b"file one", b"file two"], "title": "hello"} - assert extract_files(query, paths=[["files", ""]]) == [ - ("files[]", b"file one"), - ("files[]", b"file two"), - ] + assert extract_files(query, paths=[["files", ""]]) == [("files[]", b"file one"), ("files[]", b"file two")] assert query == {"title": "hello"} @@ -71,3 +68,24 @@ def test_ignores_incorrect_paths( expected: list[tuple[str, FileTypes]], ) -> None: assert extract_files(query, paths=paths) == expected + + +@pytest.mark.parametrize( + "array_format,expected_top_level,expected_nested", + [ + ("brackets", [("files[]", b"a"), ("files[]", b"b")], [("items[][file]", b"a"), ("items[][file]", b"b")]), + ("repeat", [("files", b"a"), ("files", b"b")], [("items[file]", b"a"), ("items[file]", b"b")]), + ("comma", [("files", b"a"), ("files", b"b")], [("items[file]", b"a"), ("items[file]", b"b")]), + ("indices", [("files[0]", b"a"), ("files[1]", b"b")], [("items[0][file]", b"a"), ("items[1][file]", b"b")]), + ], +) +def test_array_format_controls_file_field_names( + array_format: ArrayFormat, + expected_top_level: list[tuple[str, FileTypes]], + expected_nested: list[tuple[str, FileTypes]], +) -> None: + top_level = {"files": [b"a", b"b"]} + assert extract_files(top_level, paths=[["files", ""]], array_format=array_format) == expected_top_level + + nested = {"items": [{"file": b"a"}, {"file": b"b"}]} + assert extract_files(nested, paths=[["items", "", "file"]], array_format=array_format) == expected_nested diff --git a/tests/test_files.py b/tests/test_files.py index e9695db7..62632e10 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -131,7 +131,7 @@ def test_extract_files_does_not_mutate_original_nested_array_path(self) -> None: copied = deepcopy_with_paths(original, [["items", "", "file"]]) extracted = extract_files(copied, paths=[["items", "", "file"]]) - assert extracted == [("items[][file]", file1), ("items[][file]", file2)] + assert [entry for _, entry in extracted] == [file1, file2] assert original == { "items": [ {"file": file1, "extra": 1}, From 0ce56bbd77bbb7170a90b838c9dff09616dea488 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 03:38:46 +0000 Subject: [PATCH 40/82] feat: support setting headers via env --- src/brand/dev/_client.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/brand/dev/_client.py b/src/brand/dev/_client.py index 5b46246f..fa051a57 100644 --- a/src/brand/dev/_client.py +++ b/src/brand/dev/_client.py @@ -19,7 +19,11 @@ RequestOptions, not_given, ) -from ._utils import is_given, get_async_library +from ._utils import ( + is_given, + is_mapping_t, + get_async_library, +) from ._compat import cached_property from ._version import __version__ from ._streaming import Stream as Stream, AsyncStream as AsyncStream @@ -90,6 +94,15 @@ def __init__( if base_url is None: base_url = f"https://api.brand.dev/v1" + custom_headers_env = os.environ.get("BRAND_DEV_CUSTOM_HEADERS") + if custom_headers_env is not None: + parsed: dict[str, str] = {} + for line in custom_headers_env.split("\n"): + colon = line.find(":") + if colon >= 0: + parsed[line[:colon].strip()] = line[colon + 1 :].strip() + default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})} + super().__init__( version=__version__, base_url=base_url, @@ -264,6 +277,15 @@ def __init__( if base_url is None: base_url = f"https://api.brand.dev/v1" + custom_headers_env = os.environ.get("BRAND_DEV_CUSTOM_HEADERS") + if custom_headers_env is not None: + parsed: dict[str, str] = {} + for line in custom_headers_env.split("\n"): + colon = line.find(":") + if colon >= 0: + parsed[line[:colon].strip()] = line[colon + 1 :].strip() + default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})} + super().__init__( version=__version__, base_url=base_url, From e0601c92a96f87fed537c9b42b628d8b16305175 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 05:45:31 +0000 Subject: [PATCH 41/82] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index c5912706..725fb756 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-632b293af91698403da1bcaf3fb91259636c772e5071a2dd0fc63c42cb591307.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-632b293af91698403da1bcaf3fb91259636c772e5071a2dd0fc63c42cb591307.yml openapi_spec_hash: d03dc81f33ef92c74aa3b5dea35cdeae config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From fff7c0847fbfa0b8a2a95d2dd06872817552c5e1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 04:29:15 +0000 Subject: [PATCH 42/82] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 725fb756..a6057b75 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-632b293af91698403da1bcaf3fb91259636c772e5071a2dd0fc63c42cb591307.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-c651aa591ed6b1a64ece1e0e6336b2d11daed5ed258bd511edcbec96d9567d1b.yml openapi_spec_hash: d03dc81f33ef92c74aa3b5dea35cdeae config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 3e37dcaafaafaa677893794e66cec86afedb65ca Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 04:32:36 +0000 Subject: [PATCH 43/82] chore(internal): reformat pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1ac8a24c..4789c5ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -168,7 +168,7 @@ show_error_codes = true # # We also exclude our `tests` as mypy doesn't always infer # types correctly and Pyright will still catch any type errors. -exclude = ['src/brand/dev/_files.py', '_dev/.*.py', 'tests/.*'] +exclude = ["src/brand/dev/_files.py", "_dev/.*.py", "tests/.*"] strict_equality = true implicit_reexport = true From 34da1c09d1e5883a6134fba8bef1a2622a841a3d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 15:01:02 +0000 Subject: [PATCH 44/82] feat(api): api update --- .stats.yml | 4 ++-- src/brand/dev/resources/brand.py | 16 ++++++++++++++++ .../dev/types/brand_web_scrape_html_params.py | 3 +++ .../dev/types/brand_web_scrape_md_params.py | 3 +++ tests/api_resources/test_brand.py | 4 ++++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index a6057b75..6eedefc5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-c651aa591ed6b1a64ece1e0e6336b2d11daed5ed258bd511edcbec96d9567d1b.yml -openapi_spec_hash: d03dc81f33ef92c74aa3b5dea35cdeae +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-ca091b4b323baadb654b7292374b8712b6de260f3c1f8f0944caf20795f608ad.yml +openapi_spec_hash: 2bc6f537bfa055541621423722ea85f2 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index 292b331c..aada18b3 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -2134,6 +2134,7 @@ def web_scrape_html( self, *, url: str, + include_frames: bool | Omit = omit, max_age_ms: int | Omit = omit, parse_pdf: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -2149,6 +2150,8 @@ def web_scrape_html( Args: url: Full URL to scrape (must include http:// or https:// protocol) + include_frames: When true, iframes are rendered inline into the returned HTML. + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. @@ -2175,6 +2178,7 @@ def web_scrape_html( query=maybe_transform( { "url": url, + "include_frames": include_frames, "max_age_ms": max_age_ms, "parse_pdf": parse_pdf, }, @@ -2228,6 +2232,7 @@ def web_scrape_md( self, *, url: str, + include_frames: bool | Omit = omit, include_images: bool | Omit = omit, include_links: bool | Omit = omit, max_age_ms: int | Omit = omit, @@ -2248,6 +2253,8 @@ def web_scrape_md( url: Full URL to scrape into LLM usable Markdown (must include http:// or https:// protocol) + include_frames: When true, the contents of iframes are rendered to Markdown. + include_images: Include image references in Markdown output include_links: Preserve hyperlinks in Markdown output @@ -2283,6 +2290,7 @@ def web_scrape_md( query=maybe_transform( { "url": url, + "include_frames": include_frames, "include_images": include_images, "include_links": include_links, "max_age_ms": max_age_ms, @@ -4427,6 +4435,7 @@ async def web_scrape_html( self, *, url: str, + include_frames: bool | Omit = omit, max_age_ms: int | Omit = omit, parse_pdf: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -4442,6 +4451,8 @@ async def web_scrape_html( Args: url: Full URL to scrape (must include http:// or https:// protocol) + include_frames: When true, iframes are rendered inline into the returned HTML. + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. @@ -4468,6 +4479,7 @@ async def web_scrape_html( query=await async_maybe_transform( { "url": url, + "include_frames": include_frames, "max_age_ms": max_age_ms, "parse_pdf": parse_pdf, }, @@ -4523,6 +4535,7 @@ async def web_scrape_md( self, *, url: str, + include_frames: bool | Omit = omit, include_images: bool | Omit = omit, include_links: bool | Omit = omit, max_age_ms: int | Omit = omit, @@ -4543,6 +4556,8 @@ async def web_scrape_md( url: Full URL to scrape into LLM usable Markdown (must include http:// or https:// protocol) + include_frames: When true, the contents of iframes are rendered to Markdown. + include_images: Include image references in Markdown output include_links: Preserve hyperlinks in Markdown output @@ -4578,6 +4593,7 @@ async def web_scrape_md( query=await async_maybe_transform( { "url": url, + "include_frames": include_frames, "include_images": include_images, "include_links": include_links, "max_age_ms": max_age_ms, diff --git a/src/brand/dev/types/brand_web_scrape_html_params.py b/src/brand/dev/types/brand_web_scrape_html_params.py index 0cdbae15..60b8907e 100644 --- a/src/brand/dev/types/brand_web_scrape_html_params.py +++ b/src/brand/dev/types/brand_web_scrape_html_params.py @@ -13,6 +13,9 @@ class BrandWebScrapeHTMLParams(TypedDict, total=False): url: Required[str] """Full URL to scrape (must include http:// or https:// protocol)""" + include_frames: Annotated[bool, PropertyInfo(alias="includeFrames")] + """When true, iframes are rendered inline into the returned HTML.""" + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] """ Return a cached result if a prior scrape for the same parameters exists and is diff --git a/src/brand/dev/types/brand_web_scrape_md_params.py b/src/brand/dev/types/brand_web_scrape_md_params.py index c0dbeadd..1550f88f 100644 --- a/src/brand/dev/types/brand_web_scrape_md_params.py +++ b/src/brand/dev/types/brand_web_scrape_md_params.py @@ -16,6 +16,9 @@ class BrandWebScrapeMdParams(TypedDict, total=False): protocol) """ + include_frames: Annotated[bool, PropertyInfo(alias="includeFrames")] + """When true, the contents of iframes are rendered to Markdown.""" + include_images: Annotated[bool, PropertyInfo(alias="includeImages")] """Include image references in Markdown output""" diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index da9b355e..c8673b0a 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -678,6 +678,7 @@ def test_method_web_scrape_html(self, client: BrandDev) -> None: def test_method_web_scrape_html_with_all_params(self, client: BrandDev) -> None: brand = client.brand.web_scrape_html( url="https://example.com", + include_frames=True, max_age_ms=0, parse_pdf=True, ) @@ -756,6 +757,7 @@ def test_method_web_scrape_md(self, client: BrandDev) -> None: def test_method_web_scrape_md_with_all_params(self, client: BrandDev) -> None: brand = client.brand.web_scrape_md( url="https://example.com", + include_frames=True, include_images=True, include_links=True, max_age_ms=0, @@ -1485,6 +1487,7 @@ async def test_method_web_scrape_html(self, async_client: AsyncBrandDev) -> None async def test_method_web_scrape_html_with_all_params(self, async_client: AsyncBrandDev) -> None: brand = await async_client.brand.web_scrape_html( url="https://example.com", + include_frames=True, max_age_ms=0, parse_pdf=True, ) @@ -1563,6 +1566,7 @@ async def test_method_web_scrape_md(self, async_client: AsyncBrandDev) -> None: async def test_method_web_scrape_md_with_all_params(self, async_client: AsyncBrandDev) -> None: brand = await async_client.brand.web_scrape_md( url="https://example.com", + include_frames=True, include_images=True, include_links=True, max_age_ms=0, From fcd2959f013e42957386b17c540c3b69fde4b08e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 22:08:23 +0000 Subject: [PATCH 45/82] feat(api): api update --- .stats.yml | 4 +- src/brand/dev/resources/brand.py | 56 ++++++++++++++----- .../types/brand_web_scrape_images_params.py | 42 +++++++++++++- .../types/brand_web_scrape_images_response.py | 40 ++++++++++--- tests/api_resources/test_brand.py | 30 ++++++++++ 5 files changed, 145 insertions(+), 27 deletions(-) diff --git a/.stats.yml b/.stats.yml index 6eedefc5..71256d4c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-ca091b4b323baadb654b7292374b8712b6de260f3c1f8f0944caf20795f608ad.yml -openapi_spec_hash: 2bc6f537bfa055541621423722ea85f2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-f4118654961d5928e40ebe0771eb12f9aa1ca68cf9268bf3400eb1ab8b4dae1a.yml +openapi_spec_hash: ff26efd9c23ca8d4ed2c84f1716280c4 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index aada18b3..c3d950d4 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -2192,6 +2192,8 @@ def web_scrape_images( self, *, url: str, + enrichment: brand_web_scrape_images_params.Enrichment | Omit = omit, + max_age_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2199,14 +2201,20 @@ def web_scrape_images( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandWebScrapeImagesResponse: - """Scrapes all images from the given URL. - - Extracts images from img, svg, - picture/source, link, and video elements including inline SVGs, base64 data - URIs, and standard URLs. + """ + Extract image assets from a web page, including standard URLs, inline SVGs, data + URIs, responsive image sources, metadata, CSS backgrounds, video posters, and + embeds. The base request costs 1 credit; enrichment costs 1 credit per returned + image. Args: - url: Full URL to scrape images from (must include http:// or https:// protocol) + url: Page URL to inspect. Must include http:// or https://. + + enrichment: Optional per-image processing, sent as deep-object query params such as + enrichment[resolution]=true. + + max_age_ms: Reuse a cached result this many milliseconds old or newer. Default: 86400000 (1 + day). Set to 0 to bypass cache. Maximum: 2592000000 (30 days). extra_headers: Send extra headers @@ -2223,7 +2231,14 @@ def web_scrape_images( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform({"url": url}, brand_web_scrape_images_params.BrandWebScrapeImagesParams), + query=maybe_transform( + { + "url": url, + "enrichment": enrichment, + "max_age_ms": max_age_ms, + }, + brand_web_scrape_images_params.BrandWebScrapeImagesParams, + ), ), cast_to=BrandWebScrapeImagesResponse, ) @@ -4493,6 +4508,8 @@ async def web_scrape_images( self, *, url: str, + enrichment: brand_web_scrape_images_params.Enrichment | Omit = omit, + max_age_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -4500,14 +4517,20 @@ async def web_scrape_images( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandWebScrapeImagesResponse: - """Scrapes all images from the given URL. - - Extracts images from img, svg, - picture/source, link, and video elements including inline SVGs, base64 data - URIs, and standard URLs. + """ + Extract image assets from a web page, including standard URLs, inline SVGs, data + URIs, responsive image sources, metadata, CSS backgrounds, video posters, and + embeds. The base request costs 1 credit; enrichment costs 1 credit per returned + image. Args: - url: Full URL to scrape images from (must include http:// or https:// protocol) + url: Page URL to inspect. Must include http:// or https://. + + enrichment: Optional per-image processing, sent as deep-object query params such as + enrichment[resolution]=true. + + max_age_ms: Reuse a cached result this many milliseconds old or newer. Default: 86400000 (1 + day). Set to 0 to bypass cache. Maximum: 2592000000 (30 days). extra_headers: Send extra headers @@ -4525,7 +4548,12 @@ async def web_scrape_images( extra_body=extra_body, timeout=timeout, query=await async_maybe_transform( - {"url": url}, brand_web_scrape_images_params.BrandWebScrapeImagesParams + { + "url": url, + "enrichment": enrichment, + "max_age_ms": max_age_ms, + }, + brand_web_scrape_images_params.BrandWebScrapeImagesParams, ), ), cast_to=BrandWebScrapeImagesResponse, diff --git a/src/brand/dev/types/brand_web_scrape_images_params.py b/src/brand/dev/types/brand_web_scrape_images_params.py index 665c5f01..bddc211e 100644 --- a/src/brand/dev/types/brand_web_scrape_images_params.py +++ b/src/brand/dev/types/brand_web_scrape_images_params.py @@ -2,11 +2,47 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing_extensions import Required, Annotated, TypedDict -__all__ = ["BrandWebScrapeImagesParams"] +from .._utils import PropertyInfo + +__all__ = ["BrandWebScrapeImagesParams", "Enrichment"] class BrandWebScrapeImagesParams(TypedDict, total=False): url: Required[str] - """Full URL to scrape images from (must include http:// or https:// protocol)""" + """Page URL to inspect. Must include http:// or https://.""" + + enrichment: Enrichment + """ + Optional per-image processing, sent as deep-object query params such as + enrichment[resolution]=true. + """ + + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] + """Reuse a cached result this many milliseconds old or newer. + + Default: 86400000 (1 day). Set to 0 to bypass cache. Maximum: 2592000000 (30 + days). + """ + + +class Enrichment(TypedDict, total=False): + """ + Optional per-image processing, sent as deep-object query params such as enrichment[resolution]=true. + """ + + classification: bool + """Classify each image by visual asset type.""" + + hosted_url: Annotated[bool, PropertyInfo(alias="hostedUrl")] + """ + Host materializable images on the Brand.dev CDN and return their URL and MIME + type. + """ + + max_time_per_ms: Annotated[int, PropertyInfo(alias="maxTimePerMs")] + """Per-image enrichment timeout in milliseconds. Default: 6000. Maximum: 60000.""" + + resolution: bool + """Measure image width and height when possible.""" diff --git a/src/brand/dev/types/brand_web_scrape_images_response.py b/src/brand/dev/types/brand_web_scrape_images_response.py index 7f475f80..83294fd8 100644 --- a/src/brand/dev/types/brand_web_scrape_images_response.py +++ b/src/brand/dev/types/brand_web_scrape_images_response.py @@ -5,29 +5,53 @@ from .._models import BaseModel -__all__ = ["BrandWebScrapeImagesResponse", "Image"] +__all__ = ["BrandWebScrapeImagesResponse", "Image", "ImageEnrichment"] + + +class ImageEnrichment(BaseModel): + """Requested metadata for images that could be processed.""" + + height: Optional[int] = None + """Image height in pixels, when measured.""" + + mimetype: Optional[str] = None + """Detected MIME type, when hosted.""" + + type: Optional[ + Literal["photography", "illustration", "logo", "wordmark", "icon", "pattern", "graphic", "other"] + ] = None + """Visual asset category, when classified.""" + + url: Optional[str] = None + """Brand.dev CDN URL, when hosted.""" + + width: Optional[int] = None + """Image width in pixels, when measured.""" class Image(BaseModel): alt: Optional[str] = None - """Alt text of the image, or null if not present""" + """Image alt text, or null when unavailable.""" element: Literal["img", "svg", "link", "source", "video", "css", "object", "meta", "background"] - """The HTML element the image was found in""" + """Where the image was found.""" src: str - """The image source - can be a URL, inline HTML (for SVGs), or a base64 data URI""" + """Original image value: URL, inline SVG or HTML, or base64 data URI.""" type: Literal["url", "html", "base64"] - """The type/format of the src value""" + """Format of src.""" + + enrichment: Optional[ImageEnrichment] = None + """Requested metadata for images that could be processed.""" class BrandWebScrapeImagesResponse(BaseModel): images: List[Image] - """Array of scraped images""" + """Images found on the page.""" success: Literal[True] - """Indicates success""" + """Always true on success.""" url: str - """The URL that was scraped""" + """Page URL that was scraped.""" diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index c8673b0a..2dd781d4 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -718,6 +718,21 @@ def test_method_web_scrape_images(self, client: BrandDev) -> None: ) assert_matches_type(BrandWebScrapeImagesResponse, brand, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_web_scrape_images_with_all_params(self, client: BrandDev) -> None: + brand = client.brand.web_scrape_images( + url="https://example.com", + enrichment={ + "classification": True, + "hosted_url": True, + "max_time_per_ms": 1, + "resolution": True, + }, + max_age_ms=0, + ) + assert_matches_type(BrandWebScrapeImagesResponse, brand, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_raw_response_web_scrape_images(self, client: BrandDev) -> None: @@ -1527,6 +1542,21 @@ async def test_method_web_scrape_images(self, async_client: AsyncBrandDev) -> No ) assert_matches_type(BrandWebScrapeImagesResponse, brand, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_web_scrape_images_with_all_params(self, async_client: AsyncBrandDev) -> None: + brand = await async_client.brand.web_scrape_images( + url="https://example.com", + enrichment={ + "classification": True, + "hosted_url": True, + "max_time_per_ms": 1, + "resolution": True, + }, + max_age_ms=0, + ) + assert_matches_type(BrandWebScrapeImagesResponse, brand, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_raw_response_web_scrape_images(self, async_client: AsyncBrandDev) -> None: From 00262dff8700b717cdacde8d05c040c2e240b94b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 23:08:30 +0000 Subject: [PATCH 46/82] feat(api): api update --- .stats.yml | 4 +- src/brand/dev/resources/brand.py | 84 +++++++++++++++++++ .../types/brand_retrieve_by_email_params.py | 8 ++ .../types/brand_retrieve_by_isin_params.py | 8 ++ .../types/brand_retrieve_by_name_params.py | 8 ++ .../types/brand_retrieve_by_ticker_params.py | 8 ++ src/brand/dev/types/brand_retrieve_params.py | 8 ++ .../types/brand_retrieve_simplified_params.py | 8 ++ .../types/brand_web_scrape_images_params.py | 2 +- tests/api_resources/test_brand.py | 12 +++ 10 files changed, 147 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 71256d4c..399384d4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-f4118654961d5928e40ebe0771eb12f9aa1ca68cf9268bf3400eb1ab8b4dae1a.yml -openapi_spec_hash: ff26efd9c23ca8d4ed2c84f1716280c4 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-e299f3a9f343788f804eb0f7d71c609bf811bea363547ead01482e54612c3b37.yml +openapi_spec_hash: 4d17210ddd28eca4c39f89e5b864d066 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index c3d950d4..00680124 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -203,6 +203,7 @@ def retrieve( "zulu", ] | Omit = omit, + max_age_ms: int | Omit = omit, max_speed: bool | Omit = omit, timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -222,6 +223,11 @@ def retrieve( force_language: Optional parameter to force the language of the retrieved brand data. + max_age_ms: Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data. Works with all three lookup methods. @@ -249,6 +255,7 @@ def retrieve( { "domain": domain, "force_language": force_language, + "max_age_ms": max_age_ms, "max_speed": max_speed, "timeout_ms": timeout_ms, }, @@ -1154,6 +1161,7 @@ def retrieve_by_email( "zulu", ] | Omit = omit, + max_age_ms: int | Omit = omit, max_speed: bool | Omit = omit, timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1175,6 +1183,11 @@ def retrieve_by_email( force_language: Optional parameter to force the language of the retrieved brand data. + max_age_ms: Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data. @@ -1202,6 +1215,7 @@ def retrieve_by_email( { "email": email, "force_language": force_language, + "max_age_ms": max_age_ms, "max_speed": max_speed, "timeout_ms": timeout_ms, }, @@ -1338,6 +1352,7 @@ def retrieve_by_isin( "zulu", ] | Omit = omit, + max_age_ms: int | Omit = omit, max_speed: bool | Omit = omit, timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1358,6 +1373,11 @@ def retrieve_by_isin( force_language: Optional parameter to force the language of the retrieved brand data. + max_age_ms: Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data. @@ -1385,6 +1405,7 @@ def retrieve_by_isin( { "isin": isin, "force_language": force_language, + "max_age_ms": max_age_ms, "max_speed": max_speed, "timeout_ms": timeout_ms, }, @@ -1763,6 +1784,7 @@ def retrieve_by_name( "zulu", ] | Omit = omit, + max_age_ms: int | Omit = omit, max_speed: bool | Omit = omit, timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1784,6 +1806,11 @@ def retrieve_by_name( force_language: Optional parameter to force the language of the retrieved brand data. + max_age_ms: Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data. @@ -1812,6 +1839,7 @@ def retrieve_by_name( "name": name, "country_gl": country_gl, "force_language": force_language, + "max_age_ms": max_age_ms, "max_speed": max_speed, "timeout_ms": timeout_ms, }, @@ -1948,6 +1976,7 @@ def retrieve_by_ticker( "zulu", ] | Omit = omit, + max_age_ms: int | Omit = omit, max_speed: bool | Omit = omit, ticker_exchange: Literal[ "AMEX", @@ -2041,6 +2070,11 @@ def retrieve_by_ticker( force_language: Optional parameter to force the language of the retrieved brand data. + max_age_ms: Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data. @@ -2070,6 +2104,7 @@ def retrieve_by_ticker( { "ticker": ticker, "force_language": force_language, + "max_age_ms": max_age_ms, "max_speed": max_speed, "ticker_exchange": ticker_exchange, "timeout_ms": timeout_ms, @@ -2084,6 +2119,7 @@ def retrieve_simplified( self, *, domain: str, + max_age_ms: int | Omit = omit, timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -2100,6 +2136,11 @@ def retrieve_simplified( Args: domain: Domain name to retrieve simplified brand data for + max_age_ms: Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes). @@ -2122,6 +2163,7 @@ def retrieve_simplified( query=maybe_transform( { "domain": domain, + "max_age_ms": max_age_ms, "timeout_ms": timeout_ms, }, brand_retrieve_simplified_params.BrandRetrieveSimplifiedParams, @@ -2519,6 +2561,7 @@ async def retrieve( "zulu", ] | Omit = omit, + max_age_ms: int | Omit = omit, max_speed: bool | Omit = omit, timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -2538,6 +2581,11 @@ async def retrieve( force_language: Optional parameter to force the language of the retrieved brand data. + max_age_ms: Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data. Works with all three lookup methods. @@ -2565,6 +2613,7 @@ async def retrieve( { "domain": domain, "force_language": force_language, + "max_age_ms": max_age_ms, "max_speed": max_speed, "timeout_ms": timeout_ms, }, @@ -3470,6 +3519,7 @@ async def retrieve_by_email( "zulu", ] | Omit = omit, + max_age_ms: int | Omit = omit, max_speed: bool | Omit = omit, timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -3491,6 +3541,11 @@ async def retrieve_by_email( force_language: Optional parameter to force the language of the retrieved brand data. + max_age_ms: Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data. @@ -3518,6 +3573,7 @@ async def retrieve_by_email( { "email": email, "force_language": force_language, + "max_age_ms": max_age_ms, "max_speed": max_speed, "timeout_ms": timeout_ms, }, @@ -3654,6 +3710,7 @@ async def retrieve_by_isin( "zulu", ] | Omit = omit, + max_age_ms: int | Omit = omit, max_speed: bool | Omit = omit, timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -3674,6 +3731,11 @@ async def retrieve_by_isin( force_language: Optional parameter to force the language of the retrieved brand data. + max_age_ms: Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data. @@ -3701,6 +3763,7 @@ async def retrieve_by_isin( { "isin": isin, "force_language": force_language, + "max_age_ms": max_age_ms, "max_speed": max_speed, "timeout_ms": timeout_ms, }, @@ -4079,6 +4142,7 @@ async def retrieve_by_name( "zulu", ] | Omit = omit, + max_age_ms: int | Omit = omit, max_speed: bool | Omit = omit, timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -4100,6 +4164,11 @@ async def retrieve_by_name( force_language: Optional parameter to force the language of the retrieved brand data. + max_age_ms: Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data. @@ -4128,6 +4197,7 @@ async def retrieve_by_name( "name": name, "country_gl": country_gl, "force_language": force_language, + "max_age_ms": max_age_ms, "max_speed": max_speed, "timeout_ms": timeout_ms, }, @@ -4264,6 +4334,7 @@ async def retrieve_by_ticker( "zulu", ] | Omit = omit, + max_age_ms: int | Omit = omit, max_speed: bool | Omit = omit, ticker_exchange: Literal[ "AMEX", @@ -4357,6 +4428,11 @@ async def retrieve_by_ticker( force_language: Optional parameter to force the language of the retrieved brand data. + max_age_ms: Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of less comprehensive data. @@ -4386,6 +4462,7 @@ async def retrieve_by_ticker( { "ticker": ticker, "force_language": force_language, + "max_age_ms": max_age_ms, "max_speed": max_speed, "ticker_exchange": ticker_exchange, "timeout_ms": timeout_ms, @@ -4400,6 +4477,7 @@ async def retrieve_simplified( self, *, domain: str, + max_age_ms: int | Omit = omit, timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -4416,6 +4494,11 @@ async def retrieve_simplified( Args: domain: Domain name to retrieve simplified brand data for + max_age_ms: Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes). @@ -4438,6 +4521,7 @@ async def retrieve_simplified( query=await async_maybe_transform( { "domain": domain, + "max_age_ms": max_age_ms, "timeout_ms": timeout_ms, }, brand_retrieve_simplified_params.BrandRetrieveSimplifiedParams, diff --git a/src/brand/dev/types/brand_retrieve_by_email_params.py b/src/brand/dev/types/brand_retrieve_by_email_params.py index 84949e7b..580b5c1e 100644 --- a/src/brand/dev/types/brand_retrieve_by_email_params.py +++ b/src/brand/dev/types/brand_retrieve_by_email_params.py @@ -141,6 +141,14 @@ class BrandRetrieveByEmailParams(TypedDict, total=False): ] """Optional parameter to force the language of the retrieved brand data.""" + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] + """ + Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + """ + max_speed: Annotated[bool, PropertyInfo(alias="maxSpeed")] """Optional parameter to optimize the API call for maximum speed. diff --git a/src/brand/dev/types/brand_retrieve_by_isin_params.py b/src/brand/dev/types/brand_retrieve_by_isin_params.py index 5be121e4..630834f7 100644 --- a/src/brand/dev/types/brand_retrieve_by_isin_params.py +++ b/src/brand/dev/types/brand_retrieve_by_isin_params.py @@ -141,6 +141,14 @@ class BrandRetrieveByIsinParams(TypedDict, total=False): ] """Optional parameter to force the language of the retrieved brand data.""" + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] + """ + Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + """ + max_speed: Annotated[bool, PropertyInfo(alias="maxSpeed")] """Optional parameter to optimize the API call for maximum speed. diff --git a/src/brand/dev/types/brand_retrieve_by_name_params.py b/src/brand/dev/types/brand_retrieve_by_name_params.py index d7742236..972fbd06 100644 --- a/src/brand/dev/types/brand_retrieve_by_name_params.py +++ b/src/brand/dev/types/brand_retrieve_by_name_params.py @@ -386,6 +386,14 @@ class BrandRetrieveByNameParams(TypedDict, total=False): ] """Optional parameter to force the language of the retrieved brand data.""" + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] + """ + Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + """ + max_speed: Annotated[bool, PropertyInfo(alias="maxSpeed")] """Optional parameter to optimize the API call for maximum speed. diff --git a/src/brand/dev/types/brand_retrieve_by_ticker_params.py b/src/brand/dev/types/brand_retrieve_by_ticker_params.py index 9d1124f8..8ba17315 100644 --- a/src/brand/dev/types/brand_retrieve_by_ticker_params.py +++ b/src/brand/dev/types/brand_retrieve_by_ticker_params.py @@ -140,6 +140,14 @@ class BrandRetrieveByTickerParams(TypedDict, total=False): ] """Optional parameter to force the language of the retrieved brand data.""" + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] + """ + Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + """ + max_speed: Annotated[bool, PropertyInfo(alias="maxSpeed")] """Optional parameter to optimize the API call for maximum speed. diff --git a/src/brand/dev/types/brand_retrieve_params.py b/src/brand/dev/types/brand_retrieve_params.py index d25c4c0b..2812ab5e 100644 --- a/src/brand/dev/types/brand_retrieve_params.py +++ b/src/brand/dev/types/brand_retrieve_params.py @@ -140,6 +140,14 @@ class BrandRetrieveParams(TypedDict, total=False): ] """Optional parameter to force the language of the retrieved brand data.""" + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] + """ + Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + """ + max_speed: Annotated[bool, PropertyInfo(alias="maxSpeed")] """Optional parameter to optimize the API call for maximum speed. diff --git a/src/brand/dev/types/brand_retrieve_simplified_params.py b/src/brand/dev/types/brand_retrieve_simplified_params.py index b9d9cd3f..4a60ba76 100644 --- a/src/brand/dev/types/brand_retrieve_simplified_params.py +++ b/src/brand/dev/types/brand_retrieve_simplified_params.py @@ -13,6 +13,14 @@ class BrandRetrieveSimplifiedParams(TypedDict, total=False): domain: Required[str] """Domain name to retrieve simplified brand data for""" + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] + """ + Maximum age in milliseconds for cached brand data before the API performs a hard + refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) + are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 + year. + """ + timeout_ms: Annotated[int, PropertyInfo(alias="timeoutMS")] """Optional timeout in milliseconds for the request. diff --git a/src/brand/dev/types/brand_web_scrape_images_params.py b/src/brand/dev/types/brand_web_scrape_images_params.py index bddc211e..2550706c 100644 --- a/src/brand/dev/types/brand_web_scrape_images_params.py +++ b/src/brand/dev/types/brand_web_scrape_images_params.py @@ -42,7 +42,7 @@ class Enrichment(TypedDict, total=False): """ max_time_per_ms: Annotated[int, PropertyInfo(alias="maxTimePerMs")] - """Per-image enrichment timeout in milliseconds. Default: 6000. Maximum: 60000.""" + """Per-image enrichment timeout in milliseconds. Default: 30000. Maximum: 60000.""" resolution: bool """Measure image width and height when possible.""" diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index 2dd781d4..5c5100ea 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -48,6 +48,7 @@ def test_method_retrieve_with_all_params(self, client: BrandDev) -> None: brand = client.brand.retrieve( domain="domain", force_language="afrikaans", + max_age_ms=86400000, max_speed=True, timeout_ms=1000, ) @@ -454,6 +455,7 @@ def test_method_retrieve_by_email_with_all_params(self, client: BrandDev) -> Non brand = client.brand.retrieve_by_email( email="dev@stainless.com", force_language="afrikaans", + max_age_ms=86400000, max_speed=True, timeout_ms=1000, ) @@ -499,6 +501,7 @@ def test_method_retrieve_by_isin_with_all_params(self, client: BrandDev) -> None brand = client.brand.retrieve_by_isin( isin="SE60513A9993", force_language="afrikaans", + max_age_ms=86400000, max_speed=True, timeout_ms=1000, ) @@ -545,6 +548,7 @@ def test_method_retrieve_by_name_with_all_params(self, client: BrandDev) -> None name="xxx", country_gl="ad", force_language="afrikaans", + max_age_ms=86400000, max_speed=True, timeout_ms=1000, ) @@ -590,6 +594,7 @@ def test_method_retrieve_by_ticker_with_all_params(self, client: BrandDev) -> No brand = client.brand.retrieve_by_ticker( ticker="ticker", force_language="afrikaans", + max_age_ms=86400000, max_speed=True, ticker_exchange="AMEX", timeout_ms=1000, @@ -635,6 +640,7 @@ def test_method_retrieve_simplified(self, client: BrandDev) -> None: def test_method_retrieve_simplified_with_all_params(self, client: BrandDev) -> None: brand = client.brand.retrieve_simplified( domain="domain", + max_age_ms=86400000, timeout_ms=1000, ) assert_matches_type(BrandRetrieveSimplifiedResponse, brand, path=["response"]) @@ -872,6 +878,7 @@ async def test_method_retrieve_with_all_params(self, async_client: AsyncBrandDev brand = await async_client.brand.retrieve( domain="domain", force_language="afrikaans", + max_age_ms=86400000, max_speed=True, timeout_ms=1000, ) @@ -1278,6 +1285,7 @@ async def test_method_retrieve_by_email_with_all_params(self, async_client: Asyn brand = await async_client.brand.retrieve_by_email( email="dev@stainless.com", force_language="afrikaans", + max_age_ms=86400000, max_speed=True, timeout_ms=1000, ) @@ -1323,6 +1331,7 @@ async def test_method_retrieve_by_isin_with_all_params(self, async_client: Async brand = await async_client.brand.retrieve_by_isin( isin="SE60513A9993", force_language="afrikaans", + max_age_ms=86400000, max_speed=True, timeout_ms=1000, ) @@ -1369,6 +1378,7 @@ async def test_method_retrieve_by_name_with_all_params(self, async_client: Async name="xxx", country_gl="ad", force_language="afrikaans", + max_age_ms=86400000, max_speed=True, timeout_ms=1000, ) @@ -1414,6 +1424,7 @@ async def test_method_retrieve_by_ticker_with_all_params(self, async_client: Asy brand = await async_client.brand.retrieve_by_ticker( ticker="ticker", force_language="afrikaans", + max_age_ms=86400000, max_speed=True, ticker_exchange="AMEX", timeout_ms=1000, @@ -1459,6 +1470,7 @@ async def test_method_retrieve_simplified(self, async_client: AsyncBrandDev) -> async def test_method_retrieve_simplified_with_all_params(self, async_client: AsyncBrandDev) -> None: brand = await async_client.brand.retrieve_simplified( domain="domain", + max_age_ms=86400000, timeout_ms=1000, ) assert_matches_type(BrandRetrieveSimplifiedResponse, brand, path=["response"]) From 05c585cb77f9fd566eed66064eded0cceff56fba Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 02:27:07 +0000 Subject: [PATCH 47/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 399384d4..4178b182 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-e299f3a9f343788f804eb0f7d71c609bf811bea363547ead01482e54612c3b37.yml -openapi_spec_hash: 4d17210ddd28eca4c39f89e5b864d066 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-084a13c80f616458adab7dd3da1df26c7b18002770bf0c866a61fbde2ab5a1e2.yml +openapi_spec_hash: e7b22a44a4023bc6a89508368636b3a6 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From ce906ee12160bff29e64d8c140decf63c02fa104 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 03:01:45 +0000 Subject: [PATCH 48/82] feat(api): api update --- .stats.yml | 4 +- src/brand/dev/resources/brand.py | 78 ++++++++++++++++--- .../dev/types/brand_ai_product_params.py | 3 +- .../dev/types/brand_ai_products_params.py | 6 +- .../dev/types/brand_web_scrape_html_params.py | 7 ++ .../types/brand_web_scrape_images_params.py | 7 ++ .../dev/types/brand_web_scrape_md_params.py | 7 ++ .../types/brand_web_scrape_sitemap_params.py | 7 ++ tests/api_resources/test_brand.py | 8 ++ 9 files changed, 110 insertions(+), 17 deletions(-) diff --git a/.stats.yml b/.stats.yml index 4178b182..3647616d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-084a13c80f616458adab7dd3da1df26c7b18002770bf0c866a61fbde2ab5a1e2.yml -openapi_spec_hash: e7b22a44a4023bc6a89508368636b3a6 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-f930a89f5fccd44dd6548fce8ae69ab2d966dd10a3e3dfa128a8894089a34d1a.yml +openapi_spec_hash: d21ebf5fac677f08d74a9153fb57f9af config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index 00680124..d23c8b8f 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -289,8 +289,9 @@ def ai_product( younger than this many milliseconds. Defaults to 7 days (604800000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. - timeout_ms: Optional timeout in milliseconds for the request. Maximum allowed value is - 300000ms (5 minutes). + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). extra_headers: Send extra headers @@ -346,8 +347,9 @@ def ai_products( max_products: Maximum number of products to extract. - timeout_ms: Optional timeout in milliseconds for the request. Maximum allowed value is - 300000ms (5 minutes). + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). extra_headers: Send extra headers @@ -390,8 +392,9 @@ def ai_products( max_products: Maximum number of products to extract. - timeout_ms: Optional timeout in milliseconds for the request. Maximum allowed value is - 300000ms (5 minutes). + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). extra_headers: Send extra headers @@ -2179,6 +2182,7 @@ def web_scrape_html( include_frames: bool | Omit = omit, max_age_ms: int | Omit = omit, parse_pdf: bool | Omit = omit, + timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2202,6 +2206,10 @@ def web_scrape_html( returned wrapped in . When false, PDF URLs are skipped and a 400 WEBSITE_ACCESS_ERROR is returned. + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2223,6 +2231,7 @@ def web_scrape_html( "include_frames": include_frames, "max_age_ms": max_age_ms, "parse_pdf": parse_pdf, + "timeout_ms": timeout_ms, }, brand_web_scrape_html_params.BrandWebScrapeHTMLParams, ), @@ -2236,6 +2245,7 @@ def web_scrape_images( url: str, enrichment: brand_web_scrape_images_params.Enrichment | Omit = omit, max_age_ms: int | Omit = omit, + timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2258,6 +2268,10 @@ def web_scrape_images( max_age_ms: Reuse a cached result this many milliseconds old or newer. Default: 86400000 (1 day). Set to 0 to bypass cache. Maximum: 2592000000 (30 days). + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2278,6 +2292,7 @@ def web_scrape_images( "url": url, "enrichment": enrichment, "max_age_ms": max_age_ms, + "timeout_ms": timeout_ms, }, brand_web_scrape_images_params.BrandWebScrapeImagesParams, ), @@ -2295,6 +2310,7 @@ def web_scrape_md( max_age_ms: int | Omit = omit, parse_pdf: bool | Omit = omit, shorten_base64_images: bool | Omit = omit, + timeout_ms: int | Omit = omit, use_main_content_only: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -2326,6 +2342,10 @@ def web_scrape_md( shorten_base64_images: Shorten base64-encoded image data in the Markdown output + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). + use_main_content_only: Extract only the main content of the page, excluding headers, footers, sidebars, and navigation @@ -2353,6 +2373,7 @@ def web_scrape_md( "max_age_ms": max_age_ms, "parse_pdf": parse_pdf, "shorten_base64_images": shorten_base64_images, + "timeout_ms": timeout_ms, "use_main_content_only": use_main_content_only, }, brand_web_scrape_md_params.BrandWebScrapeMdParams, @@ -2366,6 +2387,7 @@ def web_scrape_sitemap( *, domain: str, max_links: int | Omit = omit, + timeout_ms: int | Omit = omit, url_regex: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -2383,6 +2405,10 @@ def web_scrape_sitemap( max_links: Maximum number of links to return from the sitemap crawl. Defaults to 10,000. Minimum is 1, maximum is 100,000. + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). + url_regex: Optional RE2-compatible regex pattern. Only URLs matching this pattern are returned and counted against maxLinks. @@ -2405,6 +2431,7 @@ def web_scrape_sitemap( { "domain": domain, "max_links": max_links, + "timeout_ms": timeout_ms, "url_regex": url_regex, }, brand_web_scrape_sitemap_params.BrandWebScrapeSitemapParams, @@ -2647,8 +2674,9 @@ async def ai_product( younger than this many milliseconds. Defaults to 7 days (604800000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. - timeout_ms: Optional timeout in milliseconds for the request. Maximum allowed value is - 300000ms (5 minutes). + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). extra_headers: Send extra headers @@ -2704,8 +2732,9 @@ async def ai_products( max_products: Maximum number of products to extract. - timeout_ms: Optional timeout in milliseconds for the request. Maximum allowed value is - 300000ms (5 minutes). + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). extra_headers: Send extra headers @@ -2748,8 +2777,9 @@ async def ai_products( max_products: Maximum number of products to extract. - timeout_ms: Optional timeout in milliseconds for the request. Maximum allowed value is - 300000ms (5 minutes). + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). extra_headers: Send extra headers @@ -4537,6 +4567,7 @@ async def web_scrape_html( include_frames: bool | Omit = omit, max_age_ms: int | Omit = omit, parse_pdf: bool | Omit = omit, + timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -4560,6 +4591,10 @@ async def web_scrape_html( returned wrapped in . When false, PDF URLs are skipped and a 400 WEBSITE_ACCESS_ERROR is returned. + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -4581,6 +4616,7 @@ async def web_scrape_html( "include_frames": include_frames, "max_age_ms": max_age_ms, "parse_pdf": parse_pdf, + "timeout_ms": timeout_ms, }, brand_web_scrape_html_params.BrandWebScrapeHTMLParams, ), @@ -4594,6 +4630,7 @@ async def web_scrape_images( url: str, enrichment: brand_web_scrape_images_params.Enrichment | Omit = omit, max_age_ms: int | Omit = omit, + timeout_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -4616,6 +4653,10 @@ async def web_scrape_images( max_age_ms: Reuse a cached result this many milliseconds old or newer. Default: 86400000 (1 day). Set to 0 to bypass cache. Maximum: 2592000000 (30 days). + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -4636,6 +4677,7 @@ async def web_scrape_images( "url": url, "enrichment": enrichment, "max_age_ms": max_age_ms, + "timeout_ms": timeout_ms, }, brand_web_scrape_images_params.BrandWebScrapeImagesParams, ), @@ -4653,6 +4695,7 @@ async def web_scrape_md( max_age_ms: int | Omit = omit, parse_pdf: bool | Omit = omit, shorten_base64_images: bool | Omit = omit, + timeout_ms: int | Omit = omit, use_main_content_only: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -4684,6 +4727,10 @@ async def web_scrape_md( shorten_base64_images: Shorten base64-encoded image data in the Markdown output + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). + use_main_content_only: Extract only the main content of the page, excluding headers, footers, sidebars, and navigation @@ -4711,6 +4758,7 @@ async def web_scrape_md( "max_age_ms": max_age_ms, "parse_pdf": parse_pdf, "shorten_base64_images": shorten_base64_images, + "timeout_ms": timeout_ms, "use_main_content_only": use_main_content_only, }, brand_web_scrape_md_params.BrandWebScrapeMdParams, @@ -4724,6 +4772,7 @@ async def web_scrape_sitemap( *, domain: str, max_links: int | Omit = omit, + timeout_ms: int | Omit = omit, url_regex: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -4741,6 +4790,10 @@ async def web_scrape_sitemap( max_links: Maximum number of links to return from the sitemap crawl. Defaults to 10,000. Minimum is 1, maximum is 100,000. + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). + url_regex: Optional RE2-compatible regex pattern. Only URLs matching this pattern are returned and counted against maxLinks. @@ -4763,6 +4816,7 @@ async def web_scrape_sitemap( { "domain": domain, "max_links": max_links, + "timeout_ms": timeout_ms, "url_regex": url_regex, }, brand_web_scrape_sitemap_params.BrandWebScrapeSitemapParams, diff --git a/src/brand/dev/types/brand_ai_product_params.py b/src/brand/dev/types/brand_ai_product_params.py index bc65ee6c..0768fe02 100644 --- a/src/brand/dev/types/brand_ai_product_params.py +++ b/src/brand/dev/types/brand_ai_product_params.py @@ -23,5 +23,6 @@ class BrandAIProductParams(TypedDict, total=False): timeout_ms: Annotated[int, PropertyInfo(alias="timeoutMS")] """Optional timeout in milliseconds for the request. - Maximum allowed value is 300000ms (5 minutes). + If the request takes longer than this value, it will be aborted with a 408 + status code. Maximum allowed value is 300000ms (5 minutes). """ diff --git a/src/brand/dev/types/brand_ai_products_params.py b/src/brand/dev/types/brand_ai_products_params.py index 978a6052..50ee523d 100644 --- a/src/brand/dev/types/brand_ai_products_params.py +++ b/src/brand/dev/types/brand_ai_products_params.py @@ -27,7 +27,8 @@ class ByDomain(TypedDict, total=False): timeout_ms: Annotated[int, PropertyInfo(alias="timeoutMS")] """Optional timeout in milliseconds for the request. - Maximum allowed value is 300000ms (5 minutes). + If the request takes longer than this value, it will be aborted with a 408 + status code. Maximum allowed value is 300000ms (5 minutes). """ @@ -51,7 +52,8 @@ class ByDirectURL(TypedDict, total=False): timeout_ms: Annotated[int, PropertyInfo(alias="timeoutMS")] """Optional timeout in milliseconds for the request. - Maximum allowed value is 300000ms (5 minutes). + If the request takes longer than this value, it will be aborted with a 408 + status code. Maximum allowed value is 300000ms (5 minutes). """ diff --git a/src/brand/dev/types/brand_web_scrape_html_params.py b/src/brand/dev/types/brand_web_scrape_html_params.py index 60b8907e..333cb69d 100644 --- a/src/brand/dev/types/brand_web_scrape_html_params.py +++ b/src/brand/dev/types/brand_web_scrape_html_params.py @@ -29,3 +29,10 @@ class BrandWebScrapeHTMLParams(TypedDict, total=False): returned wrapped in . When false, PDF URLs are skipped and a 400 WEBSITE_ACCESS_ERROR is returned. """ + + timeout_ms: Annotated[int, PropertyInfo(alias="timeoutMS")] + """Optional timeout in milliseconds for the request. + + If the request takes longer than this value, it will be aborted with a 408 + status code. Maximum allowed value is 300000ms (5 minutes). + """ diff --git a/src/brand/dev/types/brand_web_scrape_images_params.py b/src/brand/dev/types/brand_web_scrape_images_params.py index 2550706c..7f246a95 100644 --- a/src/brand/dev/types/brand_web_scrape_images_params.py +++ b/src/brand/dev/types/brand_web_scrape_images_params.py @@ -26,6 +26,13 @@ class BrandWebScrapeImagesParams(TypedDict, total=False): days). """ + timeout_ms: Annotated[int, PropertyInfo(alias="timeoutMS")] + """Optional timeout in milliseconds for the request. + + If the request takes longer than this value, it will be aborted with a 408 + status code. Maximum allowed value is 300000ms (5 minutes). + """ + class Enrichment(TypedDict, total=False): """ diff --git a/src/brand/dev/types/brand_web_scrape_md_params.py b/src/brand/dev/types/brand_web_scrape_md_params.py index 1550f88f..6d803edc 100644 --- a/src/brand/dev/types/brand_web_scrape_md_params.py +++ b/src/brand/dev/types/brand_web_scrape_md_params.py @@ -42,6 +42,13 @@ class BrandWebScrapeMdParams(TypedDict, total=False): shorten_base64_images: Annotated[bool, PropertyInfo(alias="shortenBase64Images")] """Shorten base64-encoded image data in the Markdown output""" + timeout_ms: Annotated[int, PropertyInfo(alias="timeoutMS")] + """Optional timeout in milliseconds for the request. + + If the request takes longer than this value, it will be aborted with a 408 + status code. Maximum allowed value is 300000ms (5 minutes). + """ + use_main_content_only: Annotated[bool, PropertyInfo(alias="useMainContentOnly")] """ Extract only the main content of the page, excluding headers, footers, sidebars, diff --git a/src/brand/dev/types/brand_web_scrape_sitemap_params.py b/src/brand/dev/types/brand_web_scrape_sitemap_params.py index 0a3be070..95b4bfc9 100644 --- a/src/brand/dev/types/brand_web_scrape_sitemap_params.py +++ b/src/brand/dev/types/brand_web_scrape_sitemap_params.py @@ -19,6 +19,13 @@ class BrandWebScrapeSitemapParams(TypedDict, total=False): Defaults to 10,000. Minimum is 1, maximum is 100,000. """ + timeout_ms: Annotated[int, PropertyInfo(alias="timeoutMS")] + """Optional timeout in milliseconds for the request. + + If the request takes longer than this value, it will be aborted with a 408 + status code. Maximum allowed value is 300000ms (5 minutes). + """ + url_regex: Annotated[str, PropertyInfo(alias="urlRegex")] """Optional RE2-compatible regex pattern. diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index 5c5100ea..0a2d4a35 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -687,6 +687,7 @@ def test_method_web_scrape_html_with_all_params(self, client: BrandDev) -> None: include_frames=True, max_age_ms=0, parse_pdf=True, + timeout_ms=1000, ) assert_matches_type(BrandWebScrapeHTMLResponse, brand, path=["response"]) @@ -736,6 +737,7 @@ def test_method_web_scrape_images_with_all_params(self, client: BrandDev) -> Non "resolution": True, }, max_age_ms=0, + timeout_ms=1000, ) assert_matches_type(BrandWebScrapeImagesResponse, brand, path=["response"]) @@ -784,6 +786,7 @@ def test_method_web_scrape_md_with_all_params(self, client: BrandDev) -> None: max_age_ms=0, parse_pdf=True, shorten_base64_images=True, + timeout_ms=1000, use_main_content_only=True, ) assert_matches_type(BrandWebScrapeMdResponse, brand, path=["response"]) @@ -828,6 +831,7 @@ def test_method_web_scrape_sitemap_with_all_params(self, client: BrandDev) -> No brand = client.brand.web_scrape_sitemap( domain="domain", max_links=1, + timeout_ms=1000, url_regex="^https?://[^/]+/blog/", ) assert_matches_type(BrandWebScrapeSitemapResponse, brand, path=["response"]) @@ -1517,6 +1521,7 @@ async def test_method_web_scrape_html_with_all_params(self, async_client: AsyncB include_frames=True, max_age_ms=0, parse_pdf=True, + timeout_ms=1000, ) assert_matches_type(BrandWebScrapeHTMLResponse, brand, path=["response"]) @@ -1566,6 +1571,7 @@ async def test_method_web_scrape_images_with_all_params(self, async_client: Asyn "resolution": True, }, max_age_ms=0, + timeout_ms=1000, ) assert_matches_type(BrandWebScrapeImagesResponse, brand, path=["response"]) @@ -1614,6 +1620,7 @@ async def test_method_web_scrape_md_with_all_params(self, async_client: AsyncBra max_age_ms=0, parse_pdf=True, shorten_base64_images=True, + timeout_ms=1000, use_main_content_only=True, ) assert_matches_type(BrandWebScrapeMdResponse, brand, path=["response"]) @@ -1658,6 +1665,7 @@ async def test_method_web_scrape_sitemap_with_all_params(self, async_client: Asy brand = await async_client.brand.web_scrape_sitemap( domain="domain", max_links=1, + timeout_ms=1000, url_regex="^https?://[^/]+/blog/", ) assert_matches_type(BrandWebScrapeSitemapResponse, brand, path=["response"]) From 291415a60a6ee87cc6b27364e6b71e039ccb3590 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 9 May 2026 01:35:47 +0000 Subject: [PATCH 49/82] feat(api): api update --- .stats.yml | 4 +-- src/brand/dev/resources/brand.py | 32 +++++++++++++++++++ .../dev/types/brand_web_scrape_html_params.py | 6 ++++ .../types/brand_web_scrape_images_params.py | 6 ++++ .../dev/types/brand_web_scrape_md_params.py | 6 ++++ tests/api_resources/test_brand.py | 6 ++++ 6 files changed, 58 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 3647616d..7d8efa93 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-f930a89f5fccd44dd6548fce8ae69ab2d966dd10a3e3dfa128a8894089a34d1a.yml -openapi_spec_hash: d21ebf5fac677f08d74a9153fb57f9af +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-1e7156456cea5c8586e6e3b189d0477cb84176c9d5ff3c2dce4baee36137fe8b.yml +openapi_spec_hash: a5e97fe34f248eb0927baf52debaf79a config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index d23c8b8f..0479ab7d 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -2183,6 +2183,7 @@ def web_scrape_html( max_age_ms: int | Omit = omit, parse_pdf: bool | Omit = omit, timeout_ms: int | Omit = omit, + wait_for_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2210,6 +2211,10 @@ def web_scrape_html( than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes). + wait_for_ms: + Optional browser wait time in milliseconds after initial page load. Min: 0. Max: + 30000 (30 seconds). + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2232,6 +2237,7 @@ def web_scrape_html( "max_age_ms": max_age_ms, "parse_pdf": parse_pdf, "timeout_ms": timeout_ms, + "wait_for_ms": wait_for_ms, }, brand_web_scrape_html_params.BrandWebScrapeHTMLParams, ), @@ -2246,6 +2252,7 @@ def web_scrape_images( enrichment: brand_web_scrape_images_params.Enrichment | Omit = omit, max_age_ms: int | Omit = omit, timeout_ms: int | Omit = omit, + wait_for_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2272,6 +2279,9 @@ def web_scrape_images( than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes). + wait_for_ms: Optional browser wait time in milliseconds after initial page load before + collecting images. Min: 0. Max: 30000 (30 seconds). + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2293,6 +2303,7 @@ def web_scrape_images( "enrichment": enrichment, "max_age_ms": max_age_ms, "timeout_ms": timeout_ms, + "wait_for_ms": wait_for_ms, }, brand_web_scrape_images_params.BrandWebScrapeImagesParams, ), @@ -2312,6 +2323,7 @@ def web_scrape_md( shorten_base64_images: bool | Omit = omit, timeout_ms: int | Omit = omit, use_main_content_only: bool | Omit = omit, + wait_for_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2349,6 +2361,9 @@ def web_scrape_md( use_main_content_only: Extract only the main content of the page, excluding headers, footers, sidebars, and navigation + wait_for_ms: Optional browser wait time in milliseconds after initial page load before + converting the page to Markdown. Min: 0. Max: 30000 (30 seconds). + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2375,6 +2390,7 @@ def web_scrape_md( "shorten_base64_images": shorten_base64_images, "timeout_ms": timeout_ms, "use_main_content_only": use_main_content_only, + "wait_for_ms": wait_for_ms, }, brand_web_scrape_md_params.BrandWebScrapeMdParams, ), @@ -4568,6 +4584,7 @@ async def web_scrape_html( max_age_ms: int | Omit = omit, parse_pdf: bool | Omit = omit, timeout_ms: int | Omit = omit, + wait_for_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -4595,6 +4612,10 @@ async def web_scrape_html( than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes). + wait_for_ms: + Optional browser wait time in milliseconds after initial page load. Min: 0. Max: + 30000 (30 seconds). + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -4617,6 +4638,7 @@ async def web_scrape_html( "max_age_ms": max_age_ms, "parse_pdf": parse_pdf, "timeout_ms": timeout_ms, + "wait_for_ms": wait_for_ms, }, brand_web_scrape_html_params.BrandWebScrapeHTMLParams, ), @@ -4631,6 +4653,7 @@ async def web_scrape_images( enrichment: brand_web_scrape_images_params.Enrichment | Omit = omit, max_age_ms: int | Omit = omit, timeout_ms: int | Omit = omit, + wait_for_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -4657,6 +4680,9 @@ async def web_scrape_images( than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes). + wait_for_ms: Optional browser wait time in milliseconds after initial page load before + collecting images. Min: 0. Max: 30000 (30 seconds). + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -4678,6 +4704,7 @@ async def web_scrape_images( "enrichment": enrichment, "max_age_ms": max_age_ms, "timeout_ms": timeout_ms, + "wait_for_ms": wait_for_ms, }, brand_web_scrape_images_params.BrandWebScrapeImagesParams, ), @@ -4697,6 +4724,7 @@ async def web_scrape_md( shorten_base64_images: bool | Omit = omit, timeout_ms: int | Omit = omit, use_main_content_only: bool | Omit = omit, + wait_for_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -4734,6 +4762,9 @@ async def web_scrape_md( use_main_content_only: Extract only the main content of the page, excluding headers, footers, sidebars, and navigation + wait_for_ms: Optional browser wait time in milliseconds after initial page load before + converting the page to Markdown. Min: 0. Max: 30000 (30 seconds). + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -4760,6 +4791,7 @@ async def web_scrape_md( "shorten_base64_images": shorten_base64_images, "timeout_ms": timeout_ms, "use_main_content_only": use_main_content_only, + "wait_for_ms": wait_for_ms, }, brand_web_scrape_md_params.BrandWebScrapeMdParams, ), diff --git a/src/brand/dev/types/brand_web_scrape_html_params.py b/src/brand/dev/types/brand_web_scrape_html_params.py index 333cb69d..963a22cb 100644 --- a/src/brand/dev/types/brand_web_scrape_html_params.py +++ b/src/brand/dev/types/brand_web_scrape_html_params.py @@ -36,3 +36,9 @@ class BrandWebScrapeHTMLParams(TypedDict, total=False): If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes). """ + + wait_for_ms: Annotated[int, PropertyInfo(alias="waitForMs")] + """Optional browser wait time in milliseconds after initial page load. + + Min: 0. Max: 30000 (30 seconds). + """ diff --git a/src/brand/dev/types/brand_web_scrape_images_params.py b/src/brand/dev/types/brand_web_scrape_images_params.py index 7f246a95..4ecfebe2 100644 --- a/src/brand/dev/types/brand_web_scrape_images_params.py +++ b/src/brand/dev/types/brand_web_scrape_images_params.py @@ -33,6 +33,12 @@ class BrandWebScrapeImagesParams(TypedDict, total=False): status code. Maximum allowed value is 300000ms (5 minutes). """ + wait_for_ms: Annotated[int, PropertyInfo(alias="waitForMs")] + """ + Optional browser wait time in milliseconds after initial page load before + collecting images. Min: 0. Max: 30000 (30 seconds). + """ + class Enrichment(TypedDict, total=False): """ diff --git a/src/brand/dev/types/brand_web_scrape_md_params.py b/src/brand/dev/types/brand_web_scrape_md_params.py index 6d803edc..85c72d85 100644 --- a/src/brand/dev/types/brand_web_scrape_md_params.py +++ b/src/brand/dev/types/brand_web_scrape_md_params.py @@ -54,3 +54,9 @@ class BrandWebScrapeMdParams(TypedDict, total=False): Extract only the main content of the page, excluding headers, footers, sidebars, and navigation """ + + wait_for_ms: Annotated[int, PropertyInfo(alias="waitForMs")] + """ + Optional browser wait time in milliseconds after initial page load before + converting the page to Markdown. Min: 0. Max: 30000 (30 seconds). + """ diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index 0a2d4a35..023a5086 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -688,6 +688,7 @@ def test_method_web_scrape_html_with_all_params(self, client: BrandDev) -> None: max_age_ms=0, parse_pdf=True, timeout_ms=1000, + wait_for_ms=0, ) assert_matches_type(BrandWebScrapeHTMLResponse, brand, path=["response"]) @@ -738,6 +739,7 @@ def test_method_web_scrape_images_with_all_params(self, client: BrandDev) -> Non }, max_age_ms=0, timeout_ms=1000, + wait_for_ms=0, ) assert_matches_type(BrandWebScrapeImagesResponse, brand, path=["response"]) @@ -788,6 +790,7 @@ def test_method_web_scrape_md_with_all_params(self, client: BrandDev) -> None: shorten_base64_images=True, timeout_ms=1000, use_main_content_only=True, + wait_for_ms=0, ) assert_matches_type(BrandWebScrapeMdResponse, brand, path=["response"]) @@ -1522,6 +1525,7 @@ async def test_method_web_scrape_html_with_all_params(self, async_client: AsyncB max_age_ms=0, parse_pdf=True, timeout_ms=1000, + wait_for_ms=0, ) assert_matches_type(BrandWebScrapeHTMLResponse, brand, path=["response"]) @@ -1572,6 +1576,7 @@ async def test_method_web_scrape_images_with_all_params(self, async_client: Asyn }, max_age_ms=0, timeout_ms=1000, + wait_for_ms=0, ) assert_matches_type(BrandWebScrapeImagesResponse, brand, path=["response"]) @@ -1622,6 +1627,7 @@ async def test_method_web_scrape_md_with_all_params(self, async_client: AsyncBra shorten_base64_images=True, timeout_ms=1000, use_main_content_only=True, + wait_for_ms=0, ) assert_matches_type(BrandWebScrapeMdResponse, brand, path=["response"]) From af1c0cab6151a11859136bd4cbbd8a0a200acdd5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 9 May 2026 03:48:36 +0000 Subject: [PATCH 50/82] fix(client): add missing f-string prefix in file type error message --- src/brand/dev/_files.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/brand/dev/_files.py b/src/brand/dev/_files.py index 0fdce17b..76da9e08 100644 --- a/src/brand/dev/_files.py +++ b/src/brand/dev/_files.py @@ -99,7 +99,7 @@ async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles elif is_sequence_t(files): files = [(key, await _async_transform_file(file)) for key, file in files] else: - raise TypeError("Unexpected file type input {type(files)}, expected mapping or sequence") + raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") return files From 44865c2f5a63e7d0e2e4ccd0c4d2c06206044fe8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 10 May 2026 16:06:58 +0000 Subject: [PATCH 51/82] feat(api): api update --- .stats.yml | 4 +- src/brand/dev/resources/brand.py | 36 ++++++++---------- .../dev/types/brand_web_scrape_html_params.py | 38 ++++++++++++++++--- .../dev/types/brand_web_scrape_md_params.py | 38 ++++++++++++++++--- tests/api_resources/test_brand.py | 24 ++++++++++-- 5 files changed, 102 insertions(+), 38 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7d8efa93..dd6dd370 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-1e7156456cea5c8586e6e3b189d0477cb84176c9d5ff3c2dce4baee36137fe8b.yml -openapi_spec_hash: a5e97fe34f248eb0927baf52debaf79a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-2ed135a665bd4f01ec462096a6e19597f06356121646256cee090d1156fbfd45.yml +openapi_spec_hash: 03039640f82e64d738a57f3d3af4445e config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index 0479ab7d..8307ba25 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -2181,7 +2181,7 @@ def web_scrape_html( url: str, include_frames: bool | Omit = omit, max_age_ms: int | Omit = omit, - parse_pdf: bool | Omit = omit, + pdf: brand_web_scrape_html_params.Pdf | Omit = omit, timeout_ms: int | Omit = omit, wait_for_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -2203,9 +2203,8 @@ def web_scrape_html( younger than this many milliseconds. Defaults to 1 day (86400000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. - parse_pdf: When true (default), PDF URLs are fetched and their text layer is extracted and - returned wrapped in . When false, PDF URLs are skipped - and a 400 WEBSITE_ACCESS_ERROR is returned. + pdf: PDF parsing controls. Use start/end to limit text extraction and OCR to an + inclusive 1-based page range. timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed @@ -2235,7 +2234,7 @@ def web_scrape_html( "url": url, "include_frames": include_frames, "max_age_ms": max_age_ms, - "parse_pdf": parse_pdf, + "pdf": pdf, "timeout_ms": timeout_ms, "wait_for_ms": wait_for_ms, }, @@ -2319,7 +2318,7 @@ def web_scrape_md( include_images: bool | Omit = omit, include_links: bool | Omit = omit, max_age_ms: int | Omit = omit, - parse_pdf: bool | Omit = omit, + pdf: brand_web_scrape_md_params.Pdf | Omit = omit, shorten_base64_images: bool | Omit = omit, timeout_ms: int | Omit = omit, use_main_content_only: bool | Omit = omit, @@ -2348,9 +2347,8 @@ def web_scrape_md( younger than this many milliseconds. Defaults to 1 day (86400000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. - parse_pdf: When true (default), PDF URLs are fetched and their text layer is extracted and - converted to Markdown. When false, PDF URLs are skipped and a 400 - WEBSITE_ACCESS_ERROR is returned. + pdf: PDF parsing controls. Use start/end to limit text extraction and OCR to an + inclusive 1-based page range. shorten_base64_images: Shorten base64-encoded image data in the Markdown output @@ -2386,7 +2384,7 @@ def web_scrape_md( "include_images": include_images, "include_links": include_links, "max_age_ms": max_age_ms, - "parse_pdf": parse_pdf, + "pdf": pdf, "shorten_base64_images": shorten_base64_images, "timeout_ms": timeout_ms, "use_main_content_only": use_main_content_only, @@ -4582,7 +4580,7 @@ async def web_scrape_html( url: str, include_frames: bool | Omit = omit, max_age_ms: int | Omit = omit, - parse_pdf: bool | Omit = omit, + pdf: brand_web_scrape_html_params.Pdf | Omit = omit, timeout_ms: int | Omit = omit, wait_for_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -4604,9 +4602,8 @@ async def web_scrape_html( younger than this many milliseconds. Defaults to 1 day (86400000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. - parse_pdf: When true (default), PDF URLs are fetched and their text layer is extracted and - returned wrapped in . When false, PDF URLs are skipped - and a 400 WEBSITE_ACCESS_ERROR is returned. + pdf: PDF parsing controls. Use start/end to limit text extraction and OCR to an + inclusive 1-based page range. timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed @@ -4636,7 +4633,7 @@ async def web_scrape_html( "url": url, "include_frames": include_frames, "max_age_ms": max_age_ms, - "parse_pdf": parse_pdf, + "pdf": pdf, "timeout_ms": timeout_ms, "wait_for_ms": wait_for_ms, }, @@ -4720,7 +4717,7 @@ async def web_scrape_md( include_images: bool | Omit = omit, include_links: bool | Omit = omit, max_age_ms: int | Omit = omit, - parse_pdf: bool | Omit = omit, + pdf: brand_web_scrape_md_params.Pdf | Omit = omit, shorten_base64_images: bool | Omit = omit, timeout_ms: int | Omit = omit, use_main_content_only: bool | Omit = omit, @@ -4749,9 +4746,8 @@ async def web_scrape_md( younger than this many milliseconds. Defaults to 1 day (86400000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. - parse_pdf: When true (default), PDF URLs are fetched and their text layer is extracted and - converted to Markdown. When false, PDF URLs are skipped and a 400 - WEBSITE_ACCESS_ERROR is returned. + pdf: PDF parsing controls. Use start/end to limit text extraction and OCR to an + inclusive 1-based page range. shorten_base64_images: Shorten base64-encoded image data in the Markdown output @@ -4787,7 +4783,7 @@ async def web_scrape_md( "include_images": include_images, "include_links": include_links, "max_age_ms": max_age_ms, - "parse_pdf": parse_pdf, + "pdf": pdf, "shorten_base64_images": shorten_base64_images, "timeout_ms": timeout_ms, "use_main_content_only": use_main_content_only, diff --git a/src/brand/dev/types/brand_web_scrape_html_params.py b/src/brand/dev/types/brand_web_scrape_html_params.py index 963a22cb..57cbf2ec 100644 --- a/src/brand/dev/types/brand_web_scrape_html_params.py +++ b/src/brand/dev/types/brand_web_scrape_html_params.py @@ -6,7 +6,7 @@ from .._utils import PropertyInfo -__all__ = ["BrandWebScrapeHTMLParams"] +__all__ = ["BrandWebScrapeHTMLParams", "Pdf"] class BrandWebScrapeHTMLParams(TypedDict, total=False): @@ -23,11 +23,11 @@ class BrandWebScrapeHTMLParams(TypedDict, total=False): omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. """ - parse_pdf: Annotated[bool, PropertyInfo(alias="parsePDF")] - """ - When true (default), PDF URLs are fetched and their text layer is extracted and - returned wrapped in . When false, PDF URLs are skipped - and a 400 WEBSITE_ACCESS_ERROR is returned. + pdf: Pdf + """PDF parsing controls. + + Use start/end to limit text extraction and OCR to an inclusive 1-based page + range. """ timeout_ms: Annotated[int, PropertyInfo(alias="timeoutMS")] @@ -42,3 +42,29 @@ class BrandWebScrapeHTMLParams(TypedDict, total=False): Min: 0. Max: 30000 (30 seconds). """ + + +class Pdf(TypedDict, total=False): + """PDF parsing controls. + + Use start/end to limit text extraction and OCR to an inclusive 1-based page range. + """ + + end: int + """Last 1-based PDF page to parse. + + When omitted, parsing ends at the last page. Must be greater than or equal to + start when both are provided. + """ + + should_parse: Annotated[bool, PropertyInfo(alias="shouldParse")] + """When true, PDF URLs are fetched and parsed. + + When false, PDF URLs are skipped and a 400 WEBSITE_ACCESS_ERROR is returned. + """ + + start: int + """First 1-based PDF page to parse. + + When omitted, parsing starts at the first page. + """ diff --git a/src/brand/dev/types/brand_web_scrape_md_params.py b/src/brand/dev/types/brand_web_scrape_md_params.py index 85c72d85..8d3163b5 100644 --- a/src/brand/dev/types/brand_web_scrape_md_params.py +++ b/src/brand/dev/types/brand_web_scrape_md_params.py @@ -6,7 +6,7 @@ from .._utils import PropertyInfo -__all__ = ["BrandWebScrapeMdParams"] +__all__ = ["BrandWebScrapeMdParams", "Pdf"] class BrandWebScrapeMdParams(TypedDict, total=False): @@ -32,11 +32,11 @@ class BrandWebScrapeMdParams(TypedDict, total=False): omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. """ - parse_pdf: Annotated[bool, PropertyInfo(alias="parsePDF")] - """ - When true (default), PDF URLs are fetched and their text layer is extracted and - converted to Markdown. When false, PDF URLs are skipped and a 400 - WEBSITE_ACCESS_ERROR is returned. + pdf: Pdf + """PDF parsing controls. + + Use start/end to limit text extraction and OCR to an inclusive 1-based page + range. """ shorten_base64_images: Annotated[bool, PropertyInfo(alias="shortenBase64Images")] @@ -60,3 +60,29 @@ class BrandWebScrapeMdParams(TypedDict, total=False): Optional browser wait time in milliseconds after initial page load before converting the page to Markdown. Min: 0. Max: 30000 (30 seconds). """ + + +class Pdf(TypedDict, total=False): + """PDF parsing controls. + + Use start/end to limit text extraction and OCR to an inclusive 1-based page range. + """ + + end: int + """Last 1-based PDF page to parse. + + When omitted, parsing ends at the last page. Must be greater than or equal to + start when both are provided. + """ + + should_parse: Annotated[bool, PropertyInfo(alias="shouldParse")] + """When true, PDF URLs are fetched and parsed. + + When false, PDF URLs are skipped and a 400 WEBSITE_ACCESS_ERROR is returned. + """ + + start: int + """First 1-based PDF page to parse. + + When omitted, parsing starts at the first page. + """ diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index 023a5086..4073a06f 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -686,7 +686,11 @@ def test_method_web_scrape_html_with_all_params(self, client: BrandDev) -> None: url="https://example.com", include_frames=True, max_age_ms=0, - parse_pdf=True, + pdf={ + "end": 1, + "should_parse": True, + "start": 1, + }, timeout_ms=1000, wait_for_ms=0, ) @@ -786,7 +790,11 @@ def test_method_web_scrape_md_with_all_params(self, client: BrandDev) -> None: include_images=True, include_links=True, max_age_ms=0, - parse_pdf=True, + pdf={ + "end": 1, + "should_parse": True, + "start": 1, + }, shorten_base64_images=True, timeout_ms=1000, use_main_content_only=True, @@ -1523,7 +1531,11 @@ async def test_method_web_scrape_html_with_all_params(self, async_client: AsyncB url="https://example.com", include_frames=True, max_age_ms=0, - parse_pdf=True, + pdf={ + "end": 1, + "should_parse": True, + "start": 1, + }, timeout_ms=1000, wait_for_ms=0, ) @@ -1623,7 +1635,11 @@ async def test_method_web_scrape_md_with_all_params(self, async_client: AsyncBra include_images=True, include_links=True, max_age_ms=0, - parse_pdf=True, + pdf={ + "end": 1, + "should_parse": True, + "start": 1, + }, shorten_base64_images=True, timeout_ms=1000, use_main_content_only=True, From 29139abf2050e5abcc3506cb0ce8659916351c01 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 01:42:18 +0000 Subject: [PATCH 52/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index dd6dd370..decd7711 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-2ed135a665bd4f01ec462096a6e19597f06356121646256cee090d1156fbfd45.yml -openapi_spec_hash: 03039640f82e64d738a57f3d3af4445e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-9d3ae65f98566401c376518084a07e8ff20ecd2742b26b82ba72b35f63c6b6ca.yml +openapi_spec_hash: bc97527ca3317e0665ed1bf48caf85c8 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 7f9f4bb802ec178979069845d1ad4a8d229bdafe Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 01:46:44 +0000 Subject: [PATCH 53/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index decd7711..b047dd65 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-9d3ae65f98566401c376518084a07e8ff20ecd2742b26b82ba72b35f63c6b6ca.yml -openapi_spec_hash: bc97527ca3317e0665ed1bf48caf85c8 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-1a6ed1d226f13ac921b4cc545f03accce635e59b7b3150b12cc7d53d262e513d.yml +openapi_spec_hash: 9c0246fcd737ffd9c984d4e9ebc64736 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 06b88f47143a8e1c9182ceab6bf3c1f2eca6cfdc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 03:40:14 +0000 Subject: [PATCH 54/82] feat(internal/types): support eagerly validating pydantic iterators --- src/brand/dev/_models.py | 80 ++++++++++++++++++++++++++++++++++++++++ tests/test_models.py | 60 ++++++++++++++++++++++++++++-- 2 files changed, 137 insertions(+), 3 deletions(-) diff --git a/src/brand/dev/_models.py b/src/brand/dev/_models.py index 29070e05..8c5ab260 100644 --- a/src/brand/dev/_models.py +++ b/src/brand/dev/_models.py @@ -25,7 +25,9 @@ ClassVar, Protocol, Required, + Annotated, ParamSpec, + TypeAlias, TypedDict, TypeGuard, final, @@ -79,7 +81,15 @@ from ._constants import RAW_RESPONSE_HEADER if TYPE_CHECKING: + from pydantic import GetCoreSchemaHandler, ValidatorFunctionWrapHandler + from pydantic_core import CoreSchema, core_schema from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema +else: + try: + from pydantic_core import CoreSchema, core_schema + except ImportError: + CoreSchema = None + core_schema = None __all__ = ["BaseModel", "GenericModel"] @@ -396,6 +406,76 @@ def model_dump_json( ) +class _EagerIterable(list[_T], Generic[_T]): + """ + Accepts any Iterable[T] input (including generators), consumes it + eagerly, and validates all items upfront. + + Validation preserves the original container type where possible + (e.g. a set[T] stays a set[T]). Serialization (model_dump / JSON) + always emits a list — round-tripping through model_dump() will not + restore the original container type. + """ + + @classmethod + def __get_pydantic_core_schema__( + cls, + source_type: Any, + handler: GetCoreSchemaHandler, + ) -> CoreSchema: + (item_type,) = get_args(source_type) or (Any,) + item_schema: CoreSchema = handler.generate_schema(item_type) + list_of_items_schema: CoreSchema = core_schema.list_schema(item_schema) + + return core_schema.no_info_wrap_validator_function( + cls._validate, + list_of_items_schema, + serialization=core_schema.plain_serializer_function_ser_schema( + cls._serialize, + info_arg=False, + ), + ) + + @staticmethod + def _validate(v: Iterable[_T], handler: "ValidatorFunctionWrapHandler") -> Any: + original_type: type[Any] = type(v) + + # Normalize to list so list_schema can validate each item + if isinstance(v, list): + items: list[_T] = v + else: + try: + items = list(v) + except TypeError as e: + raise TypeError("Value is not iterable") from e + + # Validate items against the inner schema + validated: list[_T] = handler(items) + + # Reconstruct original container type + if original_type is list: + return validated + # str(list) produces the list's repr, not a string built from items, + # so skip reconstruction for str and its subclasses. + if issubclass(original_type, str): + return validated + try: + return original_type(validated) + except (TypeError, ValueError): + # If the type cannot be reconstructed, just return the validated list + return validated + + @staticmethod + def _serialize(v: Iterable[_T]) -> list[_T]: + """Always serialize as a list so Pydantic's JSON encoder is happy.""" + if isinstance(v, list): + return v + return list(v) + + +EagerIterable: TypeAlias = Annotated[Iterable[_T], _EagerIterable] + + def _construct_field(value: object, field: FieldInfo, key: str) -> object: if value is None: return field_get_default(field) diff --git a/tests/test_models.py b/tests/test_models.py index 66e35654..8e9ab3a7 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,7 +1,8 @@ import json -from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional, cast +from typing import TYPE_CHECKING, Any, Dict, List, Union, Iterable, Optional, cast from datetime import datetime, timezone -from typing_extensions import Literal, Annotated, TypeAliasType +from collections import deque +from typing_extensions import Literal, Annotated, TypedDict, TypeAliasType import pytest import pydantic @@ -9,7 +10,7 @@ from brand.dev._utils import PropertyInfo from brand.dev._compat import PYDANTIC_V1, parse_obj, model_dump, model_json -from brand.dev._models import DISCRIMINATOR_CACHE, BaseModel, construct_type +from brand.dev._models import DISCRIMINATOR_CACHE, BaseModel, EagerIterable, construct_type class BasicModel(BaseModel): @@ -961,3 +962,56 @@ def __getattr__(self, attr: str) -> Item: ... assert model.a.prop == 1 assert isinstance(model.a, Item) assert model.other == "foo" + + +# NOTE: Workaround for Pydantic Iterable behavior. +# Iterable fields are replaced with a ValidatorIterator and may be consumed +# during serialization, which can cause subsequent dumps to return empty data. +# See: https://github.com/pydantic/pydantic/issues/9541 +@pytest.mark.parametrize( + "data, expected_validated", + [ + ([1, 2, 3], [1, 2, 3]), + ((1, 2, 3), (1, 2, 3)), + (set([1, 2, 3]), set([1, 2, 3])), + (iter([1, 2, 3]), [1, 2, 3]), + ([], []), + ((x for x in [1, 2, 3]), [1, 2, 3]), + (map(lambda x: x, [1, 2, 3]), [1, 2, 3]), + (frozenset([1, 2, 3]), frozenset([1, 2, 3])), + (deque([1, 2, 3]), deque([1, 2, 3])), + ], + ids=["list", "tuple", "set", "iterator", "empty", "generator", "map", "frozenset", "deque"], +) +@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2") +def test_iterable_construction(data: Iterable[int], expected_validated: Iterable[int]) -> None: + class TypeWithIterable(TypedDict): + items: EagerIterable[int] + + class Model(BaseModel): + data: TypeWithIterable + + m = Model.model_validate({"data": {"items": data}}) + assert m.data["items"] == expected_validated + + # Verify repeated dumps don't lose data (the original bug) + assert m.model_dump()["data"]["items"] == list(expected_validated) + assert m.model_dump()["data"]["items"] == list(expected_validated) + + +@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2") +def test_iterable_construction_str_falls_back_to_list() -> None: + # str is iterable (over chars), but str(list_of_chars) produces the list's repr + # rather than reconstructing a string from items. We special-case str to fall + # back to list instead of attempting reconstruction. + class TypeWithIterable(TypedDict): + items: EagerIterable[str] + + class Model(BaseModel): + data: TypeWithIterable + + m = Model.model_validate({"data": {"items": "hello"}}) + + # falls back to list of chars rather than calling str(["h", "e", "l", "l", "o"]) + assert m.data["items"] == ["h", "e", "l", "l", "o"] + assert m.model_dump()["data"]["items"] == ["h", "e", "l", "l", "o"] From 0d83ee94dcf8718a433fdf8dd6928afcabc32fb7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 02:57:16 +0000 Subject: [PATCH 55/82] ci: pin GitHub Actions to commit SHAs Pin all GitHub Actions referenced in generated workflows (both first-party `actions/*` and third-party) to immutable commit SHAs. Updating pinned actions is now a deliberate codegen-side bump rather than implicit on every workflow run. --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/publish-pypi.yml | 2 +- .github/workflows/release-doctor.yml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5115900e..5ef00cb9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/brand.dev-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Rye run: | @@ -46,7 +46,7 @@ jobs: id-token: write runs-on: ${{ github.repository == 'stainless-sdks/brand.dev-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Rye run: | @@ -67,7 +67,7 @@ jobs: github.repository == 'stainless-sdks/brand.dev-python' && !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: core.setOutput('github_token', await core.getIDToken()); @@ -87,7 +87,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/brand.dev-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Rye run: | diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 535e7fd5..d2f2691c 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Rye run: | diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 7778fb4a..d21c7a41 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -12,7 +12,7 @@ jobs: if: github.repository == 'context-dot-dev/deprecated-brand-python-sdk' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Check release environment run: | From 13d1cd821aa1e86717b219ec598c25a1fc50ea29 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 16 May 2026 16:26:11 +0000 Subject: [PATCH 56/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index b047dd65..7381cdd3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-1a6ed1d226f13ac921b4cc545f03accce635e59b7b3150b12cc7d53d262e513d.yml -openapi_spec_hash: 9c0246fcd737ffd9c984d4e9ebc64736 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-12e9deaf43f690da9fa9319d4e93e4f8f6184e09badcc83e2b1f0c4891cf6ce3.yml +openapi_spec_hash: b18735a3f4c73c37fbb60515c9b8c346 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 50382ef0c4b9bae188fac33c87828c9ff15416b9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 16 May 2026 16:50:50 +0000 Subject: [PATCH 57/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7381cdd3..5f9bc778 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-12e9deaf43f690da9fa9319d4e93e4f8f6184e09badcc83e2b1f0c4891cf6ce3.yml -openapi_spec_hash: b18735a3f4c73c37fbb60515c9b8c346 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-ea848da22c7fabe9073064f70f12a618993846ecc5dfedbc24a0326b9f1f6fdb.yml +openapi_spec_hash: 284bb75c275d0084eb650847c4076fe4 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 22fbedcb1956b8a468669f45bf7f096a405468b1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 16 May 2026 17:12:51 +0000 Subject: [PATCH 58/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 5f9bc778..b034d58e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-ea848da22c7fabe9073064f70f12a618993846ecc5dfedbc24a0326b9f1f6fdb.yml -openapi_spec_hash: 284bb75c275d0084eb650847c4076fe4 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-5cf59db203bea5af40ea44e7ffc26b503361856a760e997da46e64528dd15b46.yml +openapi_spec_hash: a1273d28c2439dae91074a21b866740c config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From d5636b5257ac28f64969d83a4803e719eea6469f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 16 May 2026 17:21:27 +0000 Subject: [PATCH 59/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index b034d58e..1d491ca6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-5cf59db203bea5af40ea44e7ffc26b503361856a760e997da46e64528dd15b46.yml -openapi_spec_hash: a1273d28c2439dae91074a21b866740c +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-cce6310a59a9676fe3aad74b5a24bbc5f1899767b71b3c912e34d50496d2d03f.yml +openapi_spec_hash: 9e97e5d4745c4fdbbc3269e9013d4094 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 90b98b60f940e7aa127cb2b52fbdf0e467cd45c1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 14:27:01 +0000 Subject: [PATCH 60/82] feat(api): api update --- .stats.yml | 4 ++-- src/brand/dev/resources/brand.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.stats.yml b/.stats.yml index 1d491ca6..18913099 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-cce6310a59a9676fe3aad74b5a24bbc5f1899767b71b3c912e34d50496d2d03f.yml -openapi_spec_hash: 9e97e5d4745c4fdbbc3269e9013d4094 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-1bc9121ee0f64a15a503b5ec2a7836edfb1e2bf561d22f598ff3cf8f40381a9c.yml +openapi_spec_hash: e4980adb7bd8a6a37a1dbb7f35585b79 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index 8307ba25..560f054d 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -2262,8 +2262,8 @@ def web_scrape_images( """ Extract image assets from a web page, including standard URLs, inline SVGs, data URIs, responsive image sources, metadata, CSS backgrounds, video posters, and - embeds. The base request costs 1 credit; enrichment costs 1 credit per returned - image. + embeds. The base request costs 1 credit. When enrichment is enabled, the entire + call costs 5 credits. Args: url: Page URL to inspect. Must include http:// or https://. @@ -4661,8 +4661,8 @@ async def web_scrape_images( """ Extract image assets from a web page, including standard URLs, inline SVGs, data URIs, responsive image sources, metadata, CSS backgrounds, video posters, and - embeds. The base request costs 1 credit; enrichment costs 1 credit per returned - image. + embeds. The base request costs 1 credit. When enrichment is enabled, the entire + call costs 5 credits. Args: url: Page URL to inspect. Must include http:// or https://. From fdbe8edbc1f080163e981dba1002b3ba7a78994f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 30 May 2026 18:04:39 +0000 Subject: [PATCH 61/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 18913099..a6a8eef5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-1bc9121ee0f64a15a503b5ec2a7836edfb1e2bf561d22f598ff3cf8f40381a9c.yml -openapi_spec_hash: e4980adb7bd8a6a37a1dbb7f35585b79 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-0b3011817c67eccaecd7deffad948f2fcd5408a04c584c3de83d69129e8bbea9.yml +openapi_spec_hash: a35cd63ce6fc1124b4ef13f63e7e29cb config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 901df9d1bd632b37831dfcd05d33bc28c8cea33c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 31 May 2026 19:16:28 +0000 Subject: [PATCH 62/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index a6a8eef5..81725ad4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-0b3011817c67eccaecd7deffad948f2fcd5408a04c584c3de83d69129e8bbea9.yml -openapi_spec_hash: a35cd63ce6fc1124b4ef13f63e7e29cb +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-f17d27c370b0b4d6ac476ac3b6bdfcb099c7c877d7f9de5e170219601dac00ba.yml +openapi_spec_hash: 89afb32832b6c8768d0150089a026e1b config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 8726cb5ea8a122cba2400c6da2e9fd44c2abe105 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 31 May 2026 20:16:09 +0000 Subject: [PATCH 63/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 81725ad4..b9027d8f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-f17d27c370b0b4d6ac476ac3b6bdfcb099c7c877d7f9de5e170219601dac00ba.yml -openapi_spec_hash: 89afb32832b6c8768d0150089a026e1b +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-10ec1bc74f69831a4fbc461c6defa14030681cbeebb351760fe8e5ce5d1c3421.yml +openapi_spec_hash: 393bb6cb95846763248d6f737271e653 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 924bc452f5a48a19b67b6db4febe02db1cb88827 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 31 May 2026 21:54:26 +0000 Subject: [PATCH 64/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index b9027d8f..12206a6f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-10ec1bc74f69831a4fbc461c6defa14030681cbeebb351760fe8e5ce5d1c3421.yml -openapi_spec_hash: 393bb6cb95846763248d6f737271e653 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-c534e82e2da624a7c3d9843652132db3791a91eef51c777d039e23e3d45f1ece.yml +openapi_spec_hash: 4c260d71b003474c4d29754cf928fc2d config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 455b22ceb78204252ea5bc797d305a84be740fd8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 31 May 2026 23:16:36 +0000 Subject: [PATCH 65/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 12206a6f..049c6727 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-c534e82e2da624a7c3d9843652132db3791a91eef51c777d039e23e3d45f1ece.yml -openapi_spec_hash: 4c260d71b003474c4d29754cf928fc2d +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-e432c523efb3bfd0f768b85458c715480ce33b2289b1505971819502e72a220c.yml +openapi_spec_hash: ddef4e7240549c27374c98bac65a44da config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From ed5604d3487c4d8752faa2a0c0ae61375fd6f5c6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 02:49:24 +0000 Subject: [PATCH 66/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 049c6727..38438188 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-e432c523efb3bfd0f768b85458c715480ce33b2289b1505971819502e72a220c.yml -openapi_spec_hash: ddef4e7240549c27374c98bac65a44da +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-e4447a53f29d624f1458d2d56b406e12b124333eda14bb67aa6497d7bbf1e705.yml +openapi_spec_hash: df5979d99fd7c4e6546c5dadcbd47b5e config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 513bbf406334ad96d3669b9c71b7e2b8b36b357f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 21:02:37 +0000 Subject: [PATCH 67/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 38438188..f805046d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-e4447a53f29d624f1458d2d56b406e12b124333eda14bb67aa6497d7bbf1e705.yml -openapi_spec_hash: df5979d99fd7c4e6546c5dadcbd47b5e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-86ee5da44d1b12e8bda41916b716e48f4c7634c539bef7bf5335f55a34616ac8.yml +openapi_spec_hash: d8c32416241035412c6b07aab88395cb config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From f611f1d80bacbed96c55cdd83bc50c069a3a42d6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2026 10:43:46 +0000 Subject: [PATCH 68/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index f805046d..41f55e40 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-86ee5da44d1b12e8bda41916b716e48f4c7634c539bef7bf5335f55a34616ac8.yml -openapi_spec_hash: d8c32416241035412c6b07aab88395cb +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-19f8905993677bfe20e73de262153f3ff68507ed685031d1514f76eaca40cd06.yml +openapi_spec_hash: d36d3ca40ef653a89660b2967d6f592e config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 572b3a8c754a9ead6c0ab766ce35beede773155a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2026 11:05:55 +0000 Subject: [PATCH 69/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 41f55e40..bc82c92d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-19f8905993677bfe20e73de262153f3ff68507ed685031d1514f76eaca40cd06.yml -openapi_spec_hash: d36d3ca40ef653a89660b2967d6f592e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-2fd1d538ce72b2e2a8cc6ab0d00638b30c2c156e5cf273d91ba6618564b2a696.yml +openapi_spec_hash: a1ba1d14a1e783d5d494d95a9188e710 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 7ac00cb7599114955153e53eddad771d4e05c1d5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2026 11:09:09 +0000 Subject: [PATCH 70/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index bc82c92d..2b9672e0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-2fd1d538ce72b2e2a8cc6ab0d00638b30c2c156e5cf273d91ba6618564b2a696.yml -openapi_spec_hash: a1ba1d14a1e783d5d494d95a9188e710 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-141a124a7556308d98115f190fc73a433ab39286ebad780604f8eb076ca23e48.yml +openapi_spec_hash: 434ca3f02912e54b70d3c85f3e317954 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 05831cd289e979dfdd04f721552b96d436b572d6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2026 11:55:06 +0000 Subject: [PATCH 71/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 2b9672e0..4c459027 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-141a124a7556308d98115f190fc73a433ab39286ebad780604f8eb076ca23e48.yml -openapi_spec_hash: 434ca3f02912e54b70d3c85f3e317954 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-68b097a33ef54d278ffead558342d6dde258f1f42ce5f74d8cff16b925dd5510.yml +openapi_spec_hash: bb9c40c477e932de35d100f4e0e85b3e config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From bb7be8c9f17e5a86aa69e7651cac1ae7b3c170c2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2026 14:07:33 +0000 Subject: [PATCH 72/82] feat(api): api update --- .stats.yml | 4 +- src/brand/dev/resources/brand.py | 50 ++++++++++++++++++- .../dev/types/brand_web_scrape_html_params.py | 8 +++ .../types/brand_web_scrape_images_params.py | 8 +++ .../dev/types/brand_web_scrape_md_params.py | 8 +++ .../types/brand_web_scrape_sitemap_params.py | 8 +++ tests/api_resources/test_brand.py | 8 +++ 7 files changed, 91 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 4c459027..a162a040 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-68b097a33ef54d278ffead558342d6dde258f1f42ce5f74d8cff16b925dd5510.yml -openapi_spec_hash: bb9c40c477e932de35d100f4e0e85b3e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-6ed467d40802bbe92c036743ad980f3cb986626e18a8e3b743e05382eee00b97.yml +openapi_spec_hash: 5fb10c717fc138f1202f8395b37713db config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index 560f054d..c5e8a67c 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Iterable +from typing import Dict, Iterable from typing_extensions import Literal, overload import httpx @@ -2179,6 +2179,7 @@ def web_scrape_html( self, *, url: str, + headers: Dict[str, str] | Omit = omit, include_frames: bool | Omit = omit, max_age_ms: int | Omit = omit, pdf: brand_web_scrape_html_params.Pdf | Omit = omit, @@ -2197,6 +2198,10 @@ def web_scrape_html( Args: url: Full URL to scrape (must include http:// or https:// protocol) + headers: Optional outbound HTTP headers forwarded only to the target URL, sent as + deep-object query params such as headers[X-Custom]=value. When provided, caching + is bypassed: the result is neither read from nor written to cache. + include_frames: When true, iframes are rendered inline into the returned HTML. max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is @@ -2232,6 +2237,7 @@ def web_scrape_html( query=maybe_transform( { "url": url, + "headers": headers, "include_frames": include_frames, "max_age_ms": max_age_ms, "pdf": pdf, @@ -2249,6 +2255,7 @@ def web_scrape_images( *, url: str, enrichment: brand_web_scrape_images_params.Enrichment | Omit = omit, + headers: Dict[str, str] | Omit = omit, max_age_ms: int | Omit = omit, timeout_ms: int | Omit = omit, wait_for_ms: int | Omit = omit, @@ -2271,6 +2278,10 @@ def web_scrape_images( enrichment: Optional per-image processing, sent as deep-object query params such as enrichment[resolution]=true. + headers: Optional outbound HTTP headers forwarded only to the target URL, sent as + deep-object query params such as headers[X-Custom]=value. When provided, caching + is bypassed: the result is neither read from nor written to cache. + max_age_ms: Reuse a cached result this many milliseconds old or newer. Default: 86400000 (1 day). Set to 0 to bypass cache. Maximum: 2592000000 (30 days). @@ -2300,6 +2311,7 @@ def web_scrape_images( { "url": url, "enrichment": enrichment, + "headers": headers, "max_age_ms": max_age_ms, "timeout_ms": timeout_ms, "wait_for_ms": wait_for_ms, @@ -2314,6 +2326,7 @@ def web_scrape_md( self, *, url: str, + headers: Dict[str, str] | Omit = omit, include_frames: bool | Omit = omit, include_images: bool | Omit = omit, include_links: bool | Omit = omit, @@ -2337,6 +2350,10 @@ def web_scrape_md( url: Full URL to scrape into LLM usable Markdown (must include http:// or https:// protocol) + headers: Optional outbound HTTP headers forwarded only to the target URL, sent as + deep-object query params such as headers[X-Custom]=value. When provided, caching + is bypassed: the result is neither read from nor written to cache. + include_frames: When true, the contents of iframes are rendered to Markdown. include_images: Include image references in Markdown output @@ -2380,6 +2397,7 @@ def web_scrape_md( query=maybe_transform( { "url": url, + "headers": headers, "include_frames": include_frames, "include_images": include_images, "include_links": include_links, @@ -2400,6 +2418,7 @@ def web_scrape_sitemap( self, *, domain: str, + headers: Dict[str, str] | Omit = omit, max_links: int | Omit = omit, timeout_ms: int | Omit = omit, url_regex: str | Omit = omit, @@ -2416,6 +2435,10 @@ def web_scrape_sitemap( Args: domain: Domain to build a sitemap for + headers: Optional outbound HTTP headers forwarded only to the target URL, sent as + deep-object query params such as headers[X-Custom]=value. When provided, caching + is bypassed: the result is neither read from nor written to cache. + max_links: Maximum number of links to return from the sitemap crawl. Defaults to 10,000. Minimum is 1, maximum is 100,000. @@ -2444,6 +2467,7 @@ def web_scrape_sitemap( query=maybe_transform( { "domain": domain, + "headers": headers, "max_links": max_links, "timeout_ms": timeout_ms, "url_regex": url_regex, @@ -4578,6 +4602,7 @@ async def web_scrape_html( self, *, url: str, + headers: Dict[str, str] | Omit = omit, include_frames: bool | Omit = omit, max_age_ms: int | Omit = omit, pdf: brand_web_scrape_html_params.Pdf | Omit = omit, @@ -4596,6 +4621,10 @@ async def web_scrape_html( Args: url: Full URL to scrape (must include http:// or https:// protocol) + headers: Optional outbound HTTP headers forwarded only to the target URL, sent as + deep-object query params such as headers[X-Custom]=value. When provided, caching + is bypassed: the result is neither read from nor written to cache. + include_frames: When true, iframes are rendered inline into the returned HTML. max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is @@ -4631,6 +4660,7 @@ async def web_scrape_html( query=await async_maybe_transform( { "url": url, + "headers": headers, "include_frames": include_frames, "max_age_ms": max_age_ms, "pdf": pdf, @@ -4648,6 +4678,7 @@ async def web_scrape_images( *, url: str, enrichment: brand_web_scrape_images_params.Enrichment | Omit = omit, + headers: Dict[str, str] | Omit = omit, max_age_ms: int | Omit = omit, timeout_ms: int | Omit = omit, wait_for_ms: int | Omit = omit, @@ -4670,6 +4701,10 @@ async def web_scrape_images( enrichment: Optional per-image processing, sent as deep-object query params such as enrichment[resolution]=true. + headers: Optional outbound HTTP headers forwarded only to the target URL, sent as + deep-object query params such as headers[X-Custom]=value. When provided, caching + is bypassed: the result is neither read from nor written to cache. + max_age_ms: Reuse a cached result this many milliseconds old or newer. Default: 86400000 (1 day). Set to 0 to bypass cache. Maximum: 2592000000 (30 days). @@ -4699,6 +4734,7 @@ async def web_scrape_images( { "url": url, "enrichment": enrichment, + "headers": headers, "max_age_ms": max_age_ms, "timeout_ms": timeout_ms, "wait_for_ms": wait_for_ms, @@ -4713,6 +4749,7 @@ async def web_scrape_md( self, *, url: str, + headers: Dict[str, str] | Omit = omit, include_frames: bool | Omit = omit, include_images: bool | Omit = omit, include_links: bool | Omit = omit, @@ -4736,6 +4773,10 @@ async def web_scrape_md( url: Full URL to scrape into LLM usable Markdown (must include http:// or https:// protocol) + headers: Optional outbound HTTP headers forwarded only to the target URL, sent as + deep-object query params such as headers[X-Custom]=value. When provided, caching + is bypassed: the result is neither read from nor written to cache. + include_frames: When true, the contents of iframes are rendered to Markdown. include_images: Include image references in Markdown output @@ -4779,6 +4820,7 @@ async def web_scrape_md( query=await async_maybe_transform( { "url": url, + "headers": headers, "include_frames": include_frames, "include_images": include_images, "include_links": include_links, @@ -4799,6 +4841,7 @@ async def web_scrape_sitemap( self, *, domain: str, + headers: Dict[str, str] | Omit = omit, max_links: int | Omit = omit, timeout_ms: int | Omit = omit, url_regex: str | Omit = omit, @@ -4815,6 +4858,10 @@ async def web_scrape_sitemap( Args: domain: Domain to build a sitemap for + headers: Optional outbound HTTP headers forwarded only to the target URL, sent as + deep-object query params such as headers[X-Custom]=value. When provided, caching + is bypassed: the result is neither read from nor written to cache. + max_links: Maximum number of links to return from the sitemap crawl. Defaults to 10,000. Minimum is 1, maximum is 100,000. @@ -4843,6 +4890,7 @@ async def web_scrape_sitemap( query=await async_maybe_transform( { "domain": domain, + "headers": headers, "max_links": max_links, "timeout_ms": timeout_ms, "url_regex": url_regex, diff --git a/src/brand/dev/types/brand_web_scrape_html_params.py b/src/brand/dev/types/brand_web_scrape_html_params.py index 57cbf2ec..c3d6507f 100644 --- a/src/brand/dev/types/brand_web_scrape_html_params.py +++ b/src/brand/dev/types/brand_web_scrape_html_params.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Dict from typing_extensions import Required, Annotated, TypedDict from .._utils import PropertyInfo @@ -13,6 +14,13 @@ class BrandWebScrapeHTMLParams(TypedDict, total=False): url: Required[str] """Full URL to scrape (must include http:// or https:// protocol)""" + headers: Dict[str, str] + """ + Optional outbound HTTP headers forwarded only to the target URL, sent as + deep-object query params such as headers[X-Custom]=value. When provided, caching + is bypassed: the result is neither read from nor written to cache. + """ + include_frames: Annotated[bool, PropertyInfo(alias="includeFrames")] """When true, iframes are rendered inline into the returned HTML.""" diff --git a/src/brand/dev/types/brand_web_scrape_images_params.py b/src/brand/dev/types/brand_web_scrape_images_params.py index 4ecfebe2..0486a376 100644 --- a/src/brand/dev/types/brand_web_scrape_images_params.py +++ b/src/brand/dev/types/brand_web_scrape_images_params.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Dict from typing_extensions import Required, Annotated, TypedDict from .._utils import PropertyInfo @@ -19,6 +20,13 @@ class BrandWebScrapeImagesParams(TypedDict, total=False): enrichment[resolution]=true. """ + headers: Dict[str, str] + """ + Optional outbound HTTP headers forwarded only to the target URL, sent as + deep-object query params such as headers[X-Custom]=value. When provided, caching + is bypassed: the result is neither read from nor written to cache. + """ + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] """Reuse a cached result this many milliseconds old or newer. diff --git a/src/brand/dev/types/brand_web_scrape_md_params.py b/src/brand/dev/types/brand_web_scrape_md_params.py index 8d3163b5..656748e0 100644 --- a/src/brand/dev/types/brand_web_scrape_md_params.py +++ b/src/brand/dev/types/brand_web_scrape_md_params.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Dict from typing_extensions import Required, Annotated, TypedDict from .._utils import PropertyInfo @@ -16,6 +17,13 @@ class BrandWebScrapeMdParams(TypedDict, total=False): protocol) """ + headers: Dict[str, str] + """ + Optional outbound HTTP headers forwarded only to the target URL, sent as + deep-object query params such as headers[X-Custom]=value. When provided, caching + is bypassed: the result is neither read from nor written to cache. + """ + include_frames: Annotated[bool, PropertyInfo(alias="includeFrames")] """When true, the contents of iframes are rendered to Markdown.""" diff --git a/src/brand/dev/types/brand_web_scrape_sitemap_params.py b/src/brand/dev/types/brand_web_scrape_sitemap_params.py index 95b4bfc9..9f35e968 100644 --- a/src/brand/dev/types/brand_web_scrape_sitemap_params.py +++ b/src/brand/dev/types/brand_web_scrape_sitemap_params.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Dict from typing_extensions import Required, Annotated, TypedDict from .._utils import PropertyInfo @@ -13,6 +14,13 @@ class BrandWebScrapeSitemapParams(TypedDict, total=False): domain: Required[str] """Domain to build a sitemap for""" + headers: Dict[str, str] + """ + Optional outbound HTTP headers forwarded only to the target URL, sent as + deep-object query params such as headers[X-Custom]=value. When provided, caching + is bypassed: the result is neither read from nor written to cache. + """ + max_links: Annotated[int, PropertyInfo(alias="maxLinks")] """Maximum number of links to return from the sitemap crawl. diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index 4073a06f..eb53bade 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -684,6 +684,7 @@ def test_method_web_scrape_html(self, client: BrandDev) -> None: def test_method_web_scrape_html_with_all_params(self, client: BrandDev) -> None: brand = client.brand.web_scrape_html( url="https://example.com", + headers={"foo": "J!"}, include_frames=True, max_age_ms=0, pdf={ @@ -741,6 +742,7 @@ def test_method_web_scrape_images_with_all_params(self, client: BrandDev) -> Non "max_time_per_ms": 1, "resolution": True, }, + headers={"foo": "J!"}, max_age_ms=0, timeout_ms=1000, wait_for_ms=0, @@ -786,6 +788,7 @@ def test_method_web_scrape_md(self, client: BrandDev) -> None: def test_method_web_scrape_md_with_all_params(self, client: BrandDev) -> None: brand = client.brand.web_scrape_md( url="https://example.com", + headers={"foo": "J!"}, include_frames=True, include_images=True, include_links=True, @@ -841,6 +844,7 @@ def test_method_web_scrape_sitemap(self, client: BrandDev) -> None: def test_method_web_scrape_sitemap_with_all_params(self, client: BrandDev) -> None: brand = client.brand.web_scrape_sitemap( domain="domain", + headers={"foo": "J!"}, max_links=1, timeout_ms=1000, url_regex="^https?://[^/]+/blog/", @@ -1529,6 +1533,7 @@ async def test_method_web_scrape_html(self, async_client: AsyncBrandDev) -> None async def test_method_web_scrape_html_with_all_params(self, async_client: AsyncBrandDev) -> None: brand = await async_client.brand.web_scrape_html( url="https://example.com", + headers={"foo": "J!"}, include_frames=True, max_age_ms=0, pdf={ @@ -1586,6 +1591,7 @@ async def test_method_web_scrape_images_with_all_params(self, async_client: Asyn "max_time_per_ms": 1, "resolution": True, }, + headers={"foo": "J!"}, max_age_ms=0, timeout_ms=1000, wait_for_ms=0, @@ -1631,6 +1637,7 @@ async def test_method_web_scrape_md(self, async_client: AsyncBrandDev) -> None: async def test_method_web_scrape_md_with_all_params(self, async_client: AsyncBrandDev) -> None: brand = await async_client.brand.web_scrape_md( url="https://example.com", + headers={"foo": "J!"}, include_frames=True, include_images=True, include_links=True, @@ -1686,6 +1693,7 @@ async def test_method_web_scrape_sitemap(self, async_client: AsyncBrandDev) -> N async def test_method_web_scrape_sitemap_with_all_params(self, async_client: AsyncBrandDev) -> None: brand = await async_client.brand.web_scrape_sitemap( domain="domain", + headers={"foo": "J!"}, max_links=1, timeout_ms=1000, url_regex="^https?://[^/]+/blog/", From 24712650d5db3900ee946cb6b449c3ff36045f20 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2026 15:16:32 +0000 Subject: [PATCH 73/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index a162a040..747631f5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-6ed467d40802bbe92c036743ad980f3cb986626e18a8e3b743e05382eee00b97.yml -openapi_spec_hash: 5fb10c717fc138f1202f8395b37713db +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-481daa177869e994747e1970f2344d2e429788465e1993bca093510c2d8aa5a9.yml +openapi_spec_hash: 860a045bcc1420095a4a77d2b4f656e2 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 4f2fb892f17828300c268e9e316477b3ee9de0b4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 7 Jun 2026 15:13:44 +0000 Subject: [PATCH 74/82] feat(api): api update --- .stats.yml | 4 ++-- .../dev/types/brand_web_scrape_html_response.py | 13 ++++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 747631f5..15445771 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-481daa177869e994747e1970f2344d2e429788465e1993bca093510c2d8aa5a9.yml -openapi_spec_hash: 860a045bcc1420095a4a77d2b4f656e2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-554674c982facf89dfb61a919d7585a1c6a18c8253a58f67f9c6d77f1defa669.yml +openapi_spec_hash: 2520368979ea4359e5d6d47de1d15eda config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/types/brand_web_scrape_html_response.py b/src/brand/dev/types/brand_web_scrape_html_response.py index a33cb79d..548a193e 100644 --- a/src/brand/dev/types/brand_web_scrape_html_response.py +++ b/src/brand/dev/types/brand_web_scrape_html_response.py @@ -9,10 +9,21 @@ class BrandWebScrapeHTMLResponse(BaseModel): html: str - """Raw HTML content of the page""" + """The scraped content of the page. + + For normal pages this is the raw HTML. When the page is a sitemap or feed served + behind an XSL stylesheet (which browsers render into HTML), this is the + underlying XML instead — see the `type` field. + """ success: Literal[True] """Indicates success""" + type: Literal["html", "xml", "json", "text", "csv", "markdown", "svg", "pdf"] + """Detected content type of the returned `html` field. + + Sitemaps and feeds are surfaced as `xml`; ordinary pages are `html`. + """ + url: str """The URL that was scraped""" From b86853684cdddc71c11371f7ac065004bd415078 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 7 Jun 2026 22:03:44 +0000 Subject: [PATCH 75/82] feat(api): api update --- .stats.yml | 4 +- src/brand/dev/resources/brand.py | 50 ++++++++++++++++++- .../dev/types/brand_web_scrape_html_params.py | 16 ++++++ .../dev/types/brand_web_scrape_md_params.py | 16 ++++++ tests/api_resources/test_brand.py | 8 +++ 5 files changed, 91 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 15445771..74edb1aa 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-554674c982facf89dfb61a919d7585a1c6a18c8253a58f67f9c6d77f1defa669.yml -openapi_spec_hash: 2520368979ea4359e5d6d47de1d15eda +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-43306d74a09e6e1f57977311aaf406e54538a4dc5a4c4ee544fa2f0deef85ab2.yml +openapi_spec_hash: 2a58ccda9d329901cac7d782ad49c848 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index c5e8a67c..aa5a1e8d 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -25,7 +25,7 @@ brand_retrieve_simplified_params, brand_identify_from_transaction_params, ) -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from .._utils import required_args, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -2179,8 +2179,10 @@ def web_scrape_html( self, *, url: str, + exclude_selectors: SequenceNotStr[str] | Omit = omit, headers: Dict[str, str] | Omit = omit, include_frames: bool | Omit = omit, + include_selectors: SequenceNotStr[str] | Omit = omit, max_age_ms: int | Omit = omit, pdf: brand_web_scrape_html_params.Pdf | Omit = omit, timeout_ms: int | Omit = omit, @@ -2198,12 +2200,20 @@ def web_scrape_html( Args: url: Full URL to scrape (must include http:// or https:// protocol) + exclude_selectors: CSS selectors to remove from the result. Applied after includeSelectors. + Exclusion takes precedence: an element matching both is removed. Examples: + "nav", "footer", ".ad-banner", "[aria-hidden=true]". + headers: Optional outbound HTTP headers forwarded only to the target URL, sent as deep-object query params such as headers[X-Custom]=value. When provided, caching is bypassed: the result is neither read from nor written to cache. include_frames: When true, iframes are rendered inline into the returned HTML. + include_selectors: CSS selectors. When provided, only matching subtrees (and their descendants) are + kept and everything else is dropped. When omitted, the entire document is kept. + Examples: "article.main", "#content", "[role=main]". + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. @@ -2237,8 +2247,10 @@ def web_scrape_html( query=maybe_transform( { "url": url, + "exclude_selectors": exclude_selectors, "headers": headers, "include_frames": include_frames, + "include_selectors": include_selectors, "max_age_ms": max_age_ms, "pdf": pdf, "timeout_ms": timeout_ms, @@ -2326,10 +2338,12 @@ def web_scrape_md( self, *, url: str, + exclude_selectors: SequenceNotStr[str] | Omit = omit, headers: Dict[str, str] | Omit = omit, include_frames: bool | Omit = omit, include_images: bool | Omit = omit, include_links: bool | Omit = omit, + include_selectors: SequenceNotStr[str] | Omit = omit, max_age_ms: int | Omit = omit, pdf: brand_web_scrape_md_params.Pdf | Omit = omit, shorten_base64_images: bool | Omit = omit, @@ -2350,6 +2364,10 @@ def web_scrape_md( url: Full URL to scrape into LLM usable Markdown (must include http:// or https:// protocol) + exclude_selectors: CSS selectors to remove before conversion to Markdown. Applied after + includeSelectors. Exclusion takes precedence: an element matching both is + removed. Examples: "nav", "footer", ".ad-banner", "[aria-hidden=true]". + headers: Optional outbound HTTP headers forwarded only to the target URL, sent as deep-object query params such as headers[X-Custom]=value. When provided, caching is bypassed: the result is neither read from nor written to cache. @@ -2360,6 +2378,10 @@ def web_scrape_md( include_links: Preserve hyperlinks in Markdown output + include_selectors: CSS selectors. When provided, only matching HTML subtrees (and their + descendants) are kept before conversion to Markdown. When omitted, the entire + document is kept. Examples: "article.main", "#content", "[role=main]". + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. @@ -2397,10 +2419,12 @@ def web_scrape_md( query=maybe_transform( { "url": url, + "exclude_selectors": exclude_selectors, "headers": headers, "include_frames": include_frames, "include_images": include_images, "include_links": include_links, + "include_selectors": include_selectors, "max_age_ms": max_age_ms, "pdf": pdf, "shorten_base64_images": shorten_base64_images, @@ -4602,8 +4626,10 @@ async def web_scrape_html( self, *, url: str, + exclude_selectors: SequenceNotStr[str] | Omit = omit, headers: Dict[str, str] | Omit = omit, include_frames: bool | Omit = omit, + include_selectors: SequenceNotStr[str] | Omit = omit, max_age_ms: int | Omit = omit, pdf: brand_web_scrape_html_params.Pdf | Omit = omit, timeout_ms: int | Omit = omit, @@ -4621,12 +4647,20 @@ async def web_scrape_html( Args: url: Full URL to scrape (must include http:// or https:// protocol) + exclude_selectors: CSS selectors to remove from the result. Applied after includeSelectors. + Exclusion takes precedence: an element matching both is removed. Examples: + "nav", "footer", ".ad-banner", "[aria-hidden=true]". + headers: Optional outbound HTTP headers forwarded only to the target URL, sent as deep-object query params such as headers[X-Custom]=value. When provided, caching is bypassed: the result is neither read from nor written to cache. include_frames: When true, iframes are rendered inline into the returned HTML. + include_selectors: CSS selectors. When provided, only matching subtrees (and their descendants) are + kept and everything else is dropped. When omitted, the entire document is kept. + Examples: "article.main", "#content", "[role=main]". + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. @@ -4660,8 +4694,10 @@ async def web_scrape_html( query=await async_maybe_transform( { "url": url, + "exclude_selectors": exclude_selectors, "headers": headers, "include_frames": include_frames, + "include_selectors": include_selectors, "max_age_ms": max_age_ms, "pdf": pdf, "timeout_ms": timeout_ms, @@ -4749,10 +4785,12 @@ async def web_scrape_md( self, *, url: str, + exclude_selectors: SequenceNotStr[str] | Omit = omit, headers: Dict[str, str] | Omit = omit, include_frames: bool | Omit = omit, include_images: bool | Omit = omit, include_links: bool | Omit = omit, + include_selectors: SequenceNotStr[str] | Omit = omit, max_age_ms: int | Omit = omit, pdf: brand_web_scrape_md_params.Pdf | Omit = omit, shorten_base64_images: bool | Omit = omit, @@ -4773,6 +4811,10 @@ async def web_scrape_md( url: Full URL to scrape into LLM usable Markdown (must include http:// or https:// protocol) + exclude_selectors: CSS selectors to remove before conversion to Markdown. Applied after + includeSelectors. Exclusion takes precedence: an element matching both is + removed. Examples: "nav", "footer", ".ad-banner", "[aria-hidden=true]". + headers: Optional outbound HTTP headers forwarded only to the target URL, sent as deep-object query params such as headers[X-Custom]=value. When provided, caching is bypassed: the result is neither read from nor written to cache. @@ -4783,6 +4825,10 @@ async def web_scrape_md( include_links: Preserve hyperlinks in Markdown output + include_selectors: CSS selectors. When provided, only matching HTML subtrees (and their + descendants) are kept before conversion to Markdown. When omitted, the entire + document is kept. Examples: "article.main", "#content", "[role=main]". + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. @@ -4820,10 +4866,12 @@ async def web_scrape_md( query=await async_maybe_transform( { "url": url, + "exclude_selectors": exclude_selectors, "headers": headers, "include_frames": include_frames, "include_images": include_images, "include_links": include_links, + "include_selectors": include_selectors, "max_age_ms": max_age_ms, "pdf": pdf, "shorten_base64_images": shorten_base64_images, diff --git a/src/brand/dev/types/brand_web_scrape_html_params.py b/src/brand/dev/types/brand_web_scrape_html_params.py index c3d6507f..17b5e86b 100644 --- a/src/brand/dev/types/brand_web_scrape_html_params.py +++ b/src/brand/dev/types/brand_web_scrape_html_params.py @@ -5,6 +5,7 @@ from typing import Dict from typing_extensions import Required, Annotated, TypedDict +from .._types import SequenceNotStr from .._utils import PropertyInfo __all__ = ["BrandWebScrapeHTMLParams", "Pdf"] @@ -14,6 +15,13 @@ class BrandWebScrapeHTMLParams(TypedDict, total=False): url: Required[str] """Full URL to scrape (must include http:// or https:// protocol)""" + exclude_selectors: Annotated[SequenceNotStr[str], PropertyInfo(alias="excludeSelectors")] + """CSS selectors to remove from the result. + + Applied after includeSelectors. Exclusion takes precedence: an element matching + both is removed. Examples: "nav", "footer", ".ad-banner", "[aria-hidden=true]". + """ + headers: Dict[str, str] """ Optional outbound HTTP headers forwarded only to the target URL, sent as @@ -24,6 +32,14 @@ class BrandWebScrapeHTMLParams(TypedDict, total=False): include_frames: Annotated[bool, PropertyInfo(alias="includeFrames")] """When true, iframes are rendered inline into the returned HTML.""" + include_selectors: Annotated[SequenceNotStr[str], PropertyInfo(alias="includeSelectors")] + """CSS selectors. + + When provided, only matching subtrees (and their descendants) are kept and + everything else is dropped. When omitted, the entire document is kept. Examples: + "article.main", "#content", "[role=main]". + """ + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] """ Return a cached result if a prior scrape for the same parameters exists and is diff --git a/src/brand/dev/types/brand_web_scrape_md_params.py b/src/brand/dev/types/brand_web_scrape_md_params.py index 656748e0..25ede46a 100644 --- a/src/brand/dev/types/brand_web_scrape_md_params.py +++ b/src/brand/dev/types/brand_web_scrape_md_params.py @@ -5,6 +5,7 @@ from typing import Dict from typing_extensions import Required, Annotated, TypedDict +from .._types import SequenceNotStr from .._utils import PropertyInfo __all__ = ["BrandWebScrapeMdParams", "Pdf"] @@ -17,6 +18,13 @@ class BrandWebScrapeMdParams(TypedDict, total=False): protocol) """ + exclude_selectors: Annotated[SequenceNotStr[str], PropertyInfo(alias="excludeSelectors")] + """CSS selectors to remove before conversion to Markdown. + + Applied after includeSelectors. Exclusion takes precedence: an element matching + both is removed. Examples: "nav", "footer", ".ad-banner", "[aria-hidden=true]". + """ + headers: Dict[str, str] """ Optional outbound HTTP headers forwarded only to the target URL, sent as @@ -33,6 +41,14 @@ class BrandWebScrapeMdParams(TypedDict, total=False): include_links: Annotated[bool, PropertyInfo(alias="includeLinks")] """Preserve hyperlinks in Markdown output""" + include_selectors: Annotated[SequenceNotStr[str], PropertyInfo(alias="includeSelectors")] + """CSS selectors. + + When provided, only matching HTML subtrees (and their descendants) are kept + before conversion to Markdown. When omitted, the entire document is kept. + Examples: "article.main", "#content", "[role=main]". + """ + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] """ Return a cached result if a prior scrape for the same parameters exists and is diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index eb53bade..f6f4a470 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -684,8 +684,10 @@ def test_method_web_scrape_html(self, client: BrandDev) -> None: def test_method_web_scrape_html_with_all_params(self, client: BrandDev) -> None: brand = client.brand.web_scrape_html( url="https://example.com", + exclude_selectors=["string"], headers={"foo": "J!"}, include_frames=True, + include_selectors=["string"], max_age_ms=0, pdf={ "end": 1, @@ -788,10 +790,12 @@ def test_method_web_scrape_md(self, client: BrandDev) -> None: def test_method_web_scrape_md_with_all_params(self, client: BrandDev) -> None: brand = client.brand.web_scrape_md( url="https://example.com", + exclude_selectors=["string"], headers={"foo": "J!"}, include_frames=True, include_images=True, include_links=True, + include_selectors=["string"], max_age_ms=0, pdf={ "end": 1, @@ -1533,8 +1537,10 @@ async def test_method_web_scrape_html(self, async_client: AsyncBrandDev) -> None async def test_method_web_scrape_html_with_all_params(self, async_client: AsyncBrandDev) -> None: brand = await async_client.brand.web_scrape_html( url="https://example.com", + exclude_selectors=["string"], headers={"foo": "J!"}, include_frames=True, + include_selectors=["string"], max_age_ms=0, pdf={ "end": 1, @@ -1637,10 +1643,12 @@ async def test_method_web_scrape_md(self, async_client: AsyncBrandDev) -> None: async def test_method_web_scrape_md_with_all_params(self, async_client: AsyncBrandDev) -> None: brand = await async_client.brand.web_scrape_md( url="https://example.com", + exclude_selectors=["string"], headers={"foo": "J!"}, include_frames=True, include_images=True, include_links=True, + include_selectors=["string"], max_age_ms=0, pdf={ "end": 1, From 38713c2f352dff90c4e9bf29af8e1acbe7cf5b3d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 20:10:18 +0000 Subject: [PATCH 76/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 74edb1aa..f2df7282 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-43306d74a09e6e1f57977311aaf406e54538a4dc5a4c4ee544fa2f0deef85ab2.yml -openapi_spec_hash: 2a58ccda9d329901cac7d782ad49c848 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-d4cdc8c2c8bb0012c2368aeae40ed20f3c93ea68dcd9892db18aa1e06642203d.yml +openapi_spec_hash: e71b8ca8fa8041c8628f82afed471199 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From b35bfa83453d3ee820e8aab7a3914545924f6df8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 21:01:28 +0000 Subject: [PATCH 77/82] feat(api): api update --- .stats.yml | 4 ++-- src/brand/dev/resources/brand.py | 10 ++++++++++ src/brand/dev/types/brand_web_scrape_html_params.py | 6 ++++++ tests/api_resources/test_brand.py | 2 ++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index f2df7282..db86bba3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-d4cdc8c2c8bb0012c2368aeae40ed20f3c93ea68dcd9892db18aa1e06642203d.yml -openapi_spec_hash: e71b8ca8fa8041c8628f82afed471199 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-661aa162bbdbb94bfa2a225feb7be095dec450591814eda5d5f7cbd10e0d131d.yml +openapi_spec_hash: 3dc831d586372da3b88e4636213de7f0 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index aa5a1e8d..d7dc09d8 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -2186,6 +2186,7 @@ def web_scrape_html( max_age_ms: int | Omit = omit, pdf: brand_web_scrape_html_params.Pdf | Omit = omit, timeout_ms: int | Omit = omit, + use_main_content_only: bool | Omit = omit, wait_for_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -2225,6 +2226,9 @@ def web_scrape_html( than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes). + use_main_content_only: When true, return only the page's main content in the HTML response, excluding + headers, footers, sidebars, and navigation when detectable. + wait_for_ms: Optional browser wait time in milliseconds after initial page load. Min: 0. Max: 30000 (30 seconds). @@ -2254,6 +2258,7 @@ def web_scrape_html( "max_age_ms": max_age_ms, "pdf": pdf, "timeout_ms": timeout_ms, + "use_main_content_only": use_main_content_only, "wait_for_ms": wait_for_ms, }, brand_web_scrape_html_params.BrandWebScrapeHTMLParams, @@ -4633,6 +4638,7 @@ async def web_scrape_html( max_age_ms: int | Omit = omit, pdf: brand_web_scrape_html_params.Pdf | Omit = omit, timeout_ms: int | Omit = omit, + use_main_content_only: bool | Omit = omit, wait_for_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -4672,6 +4678,9 @@ async def web_scrape_html( than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes). + use_main_content_only: When true, return only the page's main content in the HTML response, excluding + headers, footers, sidebars, and navigation when detectable. + wait_for_ms: Optional browser wait time in milliseconds after initial page load. Min: 0. Max: 30000 (30 seconds). @@ -4701,6 +4710,7 @@ async def web_scrape_html( "max_age_ms": max_age_ms, "pdf": pdf, "timeout_ms": timeout_ms, + "use_main_content_only": use_main_content_only, "wait_for_ms": wait_for_ms, }, brand_web_scrape_html_params.BrandWebScrapeHTMLParams, diff --git a/src/brand/dev/types/brand_web_scrape_html_params.py b/src/brand/dev/types/brand_web_scrape_html_params.py index 17b5e86b..220cf26c 100644 --- a/src/brand/dev/types/brand_web_scrape_html_params.py +++ b/src/brand/dev/types/brand_web_scrape_html_params.py @@ -61,6 +61,12 @@ class BrandWebScrapeHTMLParams(TypedDict, total=False): status code. Maximum allowed value is 300000ms (5 minutes). """ + use_main_content_only: Annotated[bool, PropertyInfo(alias="useMainContentOnly")] + """ + When true, return only the page's main content in the HTML response, excluding + headers, footers, sidebars, and navigation when detectable. + """ + wait_for_ms: Annotated[int, PropertyInfo(alias="waitForMs")] """Optional browser wait time in milliseconds after initial page load. diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index f6f4a470..43f34867 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -695,6 +695,7 @@ def test_method_web_scrape_html_with_all_params(self, client: BrandDev) -> None: "start": 1, }, timeout_ms=1000, + use_main_content_only=True, wait_for_ms=0, ) assert_matches_type(BrandWebScrapeHTMLResponse, brand, path=["response"]) @@ -1548,6 +1549,7 @@ async def test_method_web_scrape_html_with_all_params(self, async_client: AsyncB "start": 1, }, timeout_ms=1000, + use_main_content_only=True, wait_for_ms=0, ) assert_matches_type(BrandWebScrapeHTMLResponse, brand, path=["response"]) From df49a125e276593dbc4692ad6590730e326821af Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 21:48:06 +0000 Subject: [PATCH 78/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index db86bba3..5dbaf4e0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-661aa162bbdbb94bfa2a225feb7be095dec450591814eda5d5f7cbd10e0d131d.yml -openapi_spec_hash: 3dc831d586372da3b88e4636213de7f0 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-437213902ba1307c7c8733d00945a188a7aa6af9e5c1f7fa1a8ffcc0f692efc8.yml +openapi_spec_hash: 0141ce3e02c643164e5a09eb2f2e89b4 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 835e3dc7d16ab4d4d7d8e6223b7671103ea99316 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 11 Jun 2026 01:10:51 +0000 Subject: [PATCH 79/82] feat(api): api update --- .stats.yml | 4 ++-- .../dev/types/brand_ai_product_response.py | 22 ++++++++++++++++- .../dev/types/brand_ai_products_response.py | 22 ++++++++++++++++- .../dev/types/brand_ai_query_response.py | 22 ++++++++++++++++- ...rand_identify_from_transaction_response.py | 21 ++++++++++++++++ .../types/brand_prefetch_by_email_response.py | 22 ++++++++++++++++- .../dev/types/brand_prefetch_response.py | 22 ++++++++++++++++- .../types/brand_retrieve_by_email_response.py | 21 ++++++++++++++++ .../types/brand_retrieve_by_isin_response.py | 21 ++++++++++++++++ .../types/brand_retrieve_by_name_response.py | 21 ++++++++++++++++ .../brand_retrieve_by_ticker_response.py | 21 ++++++++++++++++ .../dev/types/brand_retrieve_response.py | 21 ++++++++++++++++ .../brand_retrieve_simplified_response.py | 21 ++++++++++++++++ .../types/brand_web_scrape_html_response.py | 23 +++++++++++++++++- .../types/brand_web_scrape_images_response.py | 22 ++++++++++++++++- .../dev/types/brand_web_scrape_md_response.py | 23 +++++++++++++++++- .../brand_web_scrape_sitemap_response.py | 24 +++++++++++++++++-- 17 files changed, 341 insertions(+), 12 deletions(-) diff --git a/.stats.yml b/.stats.yml index 5dbaf4e0..503623ef 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-437213902ba1307c7c8733d00945a188a7aa6af9e5c1f7fa1a8ffcc0f692efc8.yml -openapi_spec_hash: 0141ce3e02c643164e5a09eb2f2e89b4 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-5545bb30aeac61ddb7d2b84ada399e9cd1cdc531a494b41e2ccf720056bdd437.yml +openapi_spec_hash: 554a88e7acd677dd0acac34b3900e5c6 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 diff --git a/src/brand/dev/types/brand_ai_product_response.py b/src/brand/dev/types/brand_ai_product_response.py index 1880ca56..e9f04328 100644 --- a/src/brand/dev/types/brand_ai_product_response.py +++ b/src/brand/dev/types/brand_ai_product_response.py @@ -5,7 +5,20 @@ from .._models import BaseModel -__all__ = ["BrandAIProductResponse", "Product"] +__all__ = ["BrandAIProductResponse", "KeyMetadata", "Product"] + + +class KeyMetadata(BaseModel): + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the response status is not 200. + """ + + credits_consumed: int + """The number of credits consumed by this request.""" + + credits_remaining: int + """The number of credits remaining for your organization after this request.""" class Product(BaseModel): @@ -58,6 +71,13 @@ class BrandAIProductResponse(BaseModel): is_product_page: Optional[bool] = None """Whether the given URL is a product detail page""" + key_metadata: Optional[KeyMetadata] = None + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the + response status is not 200. + """ + platform: Optional[Literal["amazon", "tiktok_shop", "etsy", "generic"]] = None """The detected ecommerce platform, or null if not a product page""" diff --git a/src/brand/dev/types/brand_ai_products_response.py b/src/brand/dev/types/brand_ai_products_response.py index a0747951..eee1953a 100644 --- a/src/brand/dev/types/brand_ai_products_response.py +++ b/src/brand/dev/types/brand_ai_products_response.py @@ -5,7 +5,20 @@ from .._models import BaseModel -__all__ = ["BrandAIProductsResponse", "Product"] +__all__ = ["BrandAIProductsResponse", "KeyMetadata", "Product"] + + +class KeyMetadata(BaseModel): + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the response status is not 200. + """ + + credits_consumed: int + """The number of credits consumed by this request.""" + + credits_remaining: int + """The number of credits remaining for your organization after this request.""" class Product(BaseModel): @@ -53,5 +66,12 @@ class Product(BaseModel): class BrandAIProductsResponse(BaseModel): + key_metadata: Optional[KeyMetadata] = None + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the + response status is not 200. + """ + products: Optional[List[Product]] = None """Array of products extracted from the website""" diff --git a/src/brand/dev/types/brand_ai_query_response.py b/src/brand/dev/types/brand_ai_query_response.py index 99714586..487c2191 100644 --- a/src/brand/dev/types/brand_ai_query_response.py +++ b/src/brand/dev/types/brand_ai_query_response.py @@ -4,7 +4,7 @@ from .._models import BaseModel -__all__ = ["BrandAIQueryResponse", "DataExtracted"] +__all__ = ["BrandAIQueryResponse", "DataExtracted", "KeyMetadata"] class DataExtracted(BaseModel): @@ -19,6 +19,19 @@ class DataExtracted(BaseModel): """ +class KeyMetadata(BaseModel): + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the response status is not 200. + """ + + credits_consumed: int + """The number of credits consumed by this request.""" + + credits_remaining: int + """The number of credits remaining for your organization after this request.""" + + class BrandAIQueryResponse(BaseModel): data_extracted: Optional[List[DataExtracted]] = None """Array of extracted data points""" @@ -26,6 +39,13 @@ class BrandAIQueryResponse(BaseModel): domain: Optional[str] = None """The domain that was analyzed""" + key_metadata: Optional[KeyMetadata] = None + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the + response status is not 200. + """ + status: Optional[str] = None """Status of the response, e.g., 'ok'""" diff --git a/src/brand/dev/types/brand_identify_from_transaction_response.py b/src/brand/dev/types/brand_identify_from_transaction_response.py index 5d499eaf..6147e929 100644 --- a/src/brand/dev/types/brand_identify_from_transaction_response.py +++ b/src/brand/dev/types/brand_identify_from_transaction_response.py @@ -21,6 +21,7 @@ "BrandLogoResolution", "BrandSocial", "BrandStock", + "KeyMetadata", ] @@ -650,6 +651,19 @@ class Brand(BaseModel): """The title or name of the brand""" +class KeyMetadata(BaseModel): + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the response status is not 200. + """ + + credits_consumed: int + """The number of credits consumed by this request.""" + + credits_remaining: int + """The number of credits remaining for your organization after this request.""" + + class BrandIdentifyFromTransactionResponse(BaseModel): brand: Optional[Brand] = None """Detailed brand information""" @@ -657,5 +671,12 @@ class BrandIdentifyFromTransactionResponse(BaseModel): code: Optional[int] = None """HTTP status code""" + key_metadata: Optional[KeyMetadata] = None + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the + response status is not 200. + """ + status: Optional[str] = None """Status of the response, e.g., 'ok'""" diff --git a/src/brand/dev/types/brand_prefetch_by_email_response.py b/src/brand/dev/types/brand_prefetch_by_email_response.py index c117f7d5..47e94547 100644 --- a/src/brand/dev/types/brand_prefetch_by_email_response.py +++ b/src/brand/dev/types/brand_prefetch_by_email_response.py @@ -4,13 +4,33 @@ from .._models import BaseModel -__all__ = ["BrandPrefetchByEmailResponse"] +__all__ = ["BrandPrefetchByEmailResponse", "KeyMetadata"] + + +class KeyMetadata(BaseModel): + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the response status is not 200. + """ + + credits_consumed: int + """The number of credits consumed by this request.""" + + credits_remaining: int + """The number of credits remaining for your organization after this request.""" class BrandPrefetchByEmailResponse(BaseModel): domain: Optional[str] = None """The domain that was queued for prefetching""" + key_metadata: Optional[KeyMetadata] = None + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the + response status is not 200. + """ + message: Optional[str] = None """Success message""" diff --git a/src/brand/dev/types/brand_prefetch_response.py b/src/brand/dev/types/brand_prefetch_response.py index 4995856a..de091a9e 100644 --- a/src/brand/dev/types/brand_prefetch_response.py +++ b/src/brand/dev/types/brand_prefetch_response.py @@ -4,13 +4,33 @@ from .._models import BaseModel -__all__ = ["BrandPrefetchResponse"] +__all__ = ["BrandPrefetchResponse", "KeyMetadata"] + + +class KeyMetadata(BaseModel): + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the response status is not 200. + """ + + credits_consumed: int + """The number of credits consumed by this request.""" + + credits_remaining: int + """The number of credits remaining for your organization after this request.""" class BrandPrefetchResponse(BaseModel): domain: Optional[str] = None """The domain that was queued for prefetching""" + key_metadata: Optional[KeyMetadata] = None + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the + response status is not 200. + """ + message: Optional[str] = None """Success message""" diff --git a/src/brand/dev/types/brand_retrieve_by_email_response.py b/src/brand/dev/types/brand_retrieve_by_email_response.py index db6b8620..6fe64824 100644 --- a/src/brand/dev/types/brand_retrieve_by_email_response.py +++ b/src/brand/dev/types/brand_retrieve_by_email_response.py @@ -21,6 +21,7 @@ "BrandLogoResolution", "BrandSocial", "BrandStock", + "KeyMetadata", ] @@ -650,6 +651,19 @@ class Brand(BaseModel): """The title or name of the brand""" +class KeyMetadata(BaseModel): + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the response status is not 200. + """ + + credits_consumed: int + """The number of credits consumed by this request.""" + + credits_remaining: int + """The number of credits remaining for your organization after this request.""" + + class BrandRetrieveByEmailResponse(BaseModel): brand: Optional[Brand] = None """Detailed brand information""" @@ -657,5 +671,12 @@ class BrandRetrieveByEmailResponse(BaseModel): code: Optional[int] = None """HTTP status code""" + key_metadata: Optional[KeyMetadata] = None + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the + response status is not 200. + """ + status: Optional[str] = None """Status of the response, e.g., 'ok'""" diff --git a/src/brand/dev/types/brand_retrieve_by_isin_response.py b/src/brand/dev/types/brand_retrieve_by_isin_response.py index 3d080e9d..8934a1f7 100644 --- a/src/brand/dev/types/brand_retrieve_by_isin_response.py +++ b/src/brand/dev/types/brand_retrieve_by_isin_response.py @@ -21,6 +21,7 @@ "BrandLogoResolution", "BrandSocial", "BrandStock", + "KeyMetadata", ] @@ -650,6 +651,19 @@ class Brand(BaseModel): """The title or name of the brand""" +class KeyMetadata(BaseModel): + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the response status is not 200. + """ + + credits_consumed: int + """The number of credits consumed by this request.""" + + credits_remaining: int + """The number of credits remaining for your organization after this request.""" + + class BrandRetrieveByIsinResponse(BaseModel): brand: Optional[Brand] = None """Detailed brand information""" @@ -657,5 +671,12 @@ class BrandRetrieveByIsinResponse(BaseModel): code: Optional[int] = None """HTTP status code""" + key_metadata: Optional[KeyMetadata] = None + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the + response status is not 200. + """ + status: Optional[str] = None """Status of the response, e.g., 'ok'""" diff --git a/src/brand/dev/types/brand_retrieve_by_name_response.py b/src/brand/dev/types/brand_retrieve_by_name_response.py index 9a5b18e5..3830c92a 100644 --- a/src/brand/dev/types/brand_retrieve_by_name_response.py +++ b/src/brand/dev/types/brand_retrieve_by_name_response.py @@ -21,6 +21,7 @@ "BrandLogoResolution", "BrandSocial", "BrandStock", + "KeyMetadata", ] @@ -650,6 +651,19 @@ class Brand(BaseModel): """The title or name of the brand""" +class KeyMetadata(BaseModel): + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the response status is not 200. + """ + + credits_consumed: int + """The number of credits consumed by this request.""" + + credits_remaining: int + """The number of credits remaining for your organization after this request.""" + + class BrandRetrieveByNameResponse(BaseModel): brand: Optional[Brand] = None """Detailed brand information""" @@ -657,5 +671,12 @@ class BrandRetrieveByNameResponse(BaseModel): code: Optional[int] = None """HTTP status code""" + key_metadata: Optional[KeyMetadata] = None + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the + response status is not 200. + """ + status: Optional[str] = None """Status of the response, e.g., 'ok'""" diff --git a/src/brand/dev/types/brand_retrieve_by_ticker_response.py b/src/brand/dev/types/brand_retrieve_by_ticker_response.py index 964d7a45..63ba9a1f 100644 --- a/src/brand/dev/types/brand_retrieve_by_ticker_response.py +++ b/src/brand/dev/types/brand_retrieve_by_ticker_response.py @@ -21,6 +21,7 @@ "BrandLogoResolution", "BrandSocial", "BrandStock", + "KeyMetadata", ] @@ -650,6 +651,19 @@ class Brand(BaseModel): """The title or name of the brand""" +class KeyMetadata(BaseModel): + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the response status is not 200. + """ + + credits_consumed: int + """The number of credits consumed by this request.""" + + credits_remaining: int + """The number of credits remaining for your organization after this request.""" + + class BrandRetrieveByTickerResponse(BaseModel): brand: Optional[Brand] = None """Detailed brand information""" @@ -657,5 +671,12 @@ class BrandRetrieveByTickerResponse(BaseModel): code: Optional[int] = None """HTTP status code""" + key_metadata: Optional[KeyMetadata] = None + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the + response status is not 200. + """ + status: Optional[str] = None """Status of the response, e.g., 'ok'""" diff --git a/src/brand/dev/types/brand_retrieve_response.py b/src/brand/dev/types/brand_retrieve_response.py index d272dab6..513402ca 100644 --- a/src/brand/dev/types/brand_retrieve_response.py +++ b/src/brand/dev/types/brand_retrieve_response.py @@ -21,6 +21,7 @@ "BrandLogoResolution", "BrandSocial", "BrandStock", + "KeyMetadata", ] @@ -650,6 +651,19 @@ class Brand(BaseModel): """The title or name of the brand""" +class KeyMetadata(BaseModel): + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the response status is not 200. + """ + + credits_consumed: int + """The number of credits consumed by this request.""" + + credits_remaining: int + """The number of credits remaining for your organization after this request.""" + + class BrandRetrieveResponse(BaseModel): brand: Optional[Brand] = None """Detailed brand information""" @@ -657,5 +671,12 @@ class BrandRetrieveResponse(BaseModel): code: Optional[int] = None """HTTP status code""" + key_metadata: Optional[KeyMetadata] = None + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the + response status is not 200. + """ + status: Optional[str] = None """Status of the response, e.g., 'ok'""" diff --git a/src/brand/dev/types/brand_retrieve_simplified_response.py b/src/brand/dev/types/brand_retrieve_simplified_response.py index 819e408d..6684272d 100644 --- a/src/brand/dev/types/brand_retrieve_simplified_response.py +++ b/src/brand/dev/types/brand_retrieve_simplified_response.py @@ -15,6 +15,7 @@ "BrandLogo", "BrandLogoColor", "BrandLogoResolution", + "KeyMetadata", ] @@ -119,6 +120,19 @@ class Brand(BaseModel): """The title or name of the brand""" +class KeyMetadata(BaseModel): + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the response status is not 200. + """ + + credits_consumed: int + """The number of credits consumed by this request.""" + + credits_remaining: int + """The number of credits remaining for your organization after this request.""" + + class BrandRetrieveSimplifiedResponse(BaseModel): brand: Optional[Brand] = None """Simplified brand information""" @@ -126,5 +140,12 @@ class BrandRetrieveSimplifiedResponse(BaseModel): code: Optional[int] = None """HTTP status code of the response""" + key_metadata: Optional[KeyMetadata] = None + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the + response status is not 200. + """ + status: Optional[str] = None """Status of the response, e.g., 'ok'""" diff --git a/src/brand/dev/types/brand_web_scrape_html_response.py b/src/brand/dev/types/brand_web_scrape_html_response.py index 548a193e..122f1e81 100644 --- a/src/brand/dev/types/brand_web_scrape_html_response.py +++ b/src/brand/dev/types/brand_web_scrape_html_response.py @@ -1,10 +1,24 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from typing_extensions import Literal from .._models import BaseModel -__all__ = ["BrandWebScrapeHTMLResponse"] +__all__ = ["BrandWebScrapeHTMLResponse", "KeyMetadata"] + + +class KeyMetadata(BaseModel): + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the response status is not 200. + """ + + credits_consumed: int + """The number of credits consumed by this request.""" + + credits_remaining: int + """The number of credits remaining for your organization after this request.""" class BrandWebScrapeHTMLResponse(BaseModel): @@ -27,3 +41,10 @@ class BrandWebScrapeHTMLResponse(BaseModel): url: str """The URL that was scraped""" + + key_metadata: Optional[KeyMetadata] = None + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the + response status is not 200. + """ diff --git a/src/brand/dev/types/brand_web_scrape_images_response.py b/src/brand/dev/types/brand_web_scrape_images_response.py index 83294fd8..cbd0b8bb 100644 --- a/src/brand/dev/types/brand_web_scrape_images_response.py +++ b/src/brand/dev/types/brand_web_scrape_images_response.py @@ -5,7 +5,7 @@ from .._models import BaseModel -__all__ = ["BrandWebScrapeImagesResponse", "Image", "ImageEnrichment"] +__all__ = ["BrandWebScrapeImagesResponse", "Image", "ImageEnrichment", "KeyMetadata"] class ImageEnrichment(BaseModel): @@ -46,6 +46,19 @@ class Image(BaseModel): """Requested metadata for images that could be processed.""" +class KeyMetadata(BaseModel): + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the response status is not 200. + """ + + credits_consumed: int + """The number of credits consumed by this request.""" + + credits_remaining: int + """The number of credits remaining for your organization after this request.""" + + class BrandWebScrapeImagesResponse(BaseModel): images: List[Image] """Images found on the page.""" @@ -55,3 +68,10 @@ class BrandWebScrapeImagesResponse(BaseModel): url: str """Page URL that was scraped.""" + + key_metadata: Optional[KeyMetadata] = None + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the + response status is not 200. + """ diff --git a/src/brand/dev/types/brand_web_scrape_md_response.py b/src/brand/dev/types/brand_web_scrape_md_response.py index 51612434..755c931b 100644 --- a/src/brand/dev/types/brand_web_scrape_md_response.py +++ b/src/brand/dev/types/brand_web_scrape_md_response.py @@ -1,10 +1,24 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from typing_extensions import Literal from .._models import BaseModel -__all__ = ["BrandWebScrapeMdResponse"] +__all__ = ["BrandWebScrapeMdResponse", "KeyMetadata"] + + +class KeyMetadata(BaseModel): + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the response status is not 200. + """ + + credits_consumed: int + """The number of credits consumed by this request.""" + + credits_remaining: int + """The number of credits remaining for your organization after this request.""" class BrandWebScrapeMdResponse(BaseModel): @@ -16,3 +30,10 @@ class BrandWebScrapeMdResponse(BaseModel): url: str """The URL that was scraped""" + + key_metadata: Optional[KeyMetadata] = None + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the + response status is not 200. + """ diff --git a/src/brand/dev/types/brand_web_scrape_sitemap_response.py b/src/brand/dev/types/brand_web_scrape_sitemap_response.py index ae7562e3..b5489f65 100644 --- a/src/brand/dev/types/brand_web_scrape_sitemap_response.py +++ b/src/brand/dev/types/brand_web_scrape_sitemap_response.py @@ -1,13 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List +from typing import List, Optional from typing_extensions import Literal from pydantic import Field as FieldInfo from .._models import BaseModel -__all__ = ["BrandWebScrapeSitemapResponse", "Meta"] +__all__ = ["BrandWebScrapeSitemapResponse", "Meta", "KeyMetadata"] class Meta(BaseModel): @@ -26,6 +26,19 @@ class Meta(BaseModel): """Number of sitemap files skipped (due to errors, timeouts, or limits)""" +class KeyMetadata(BaseModel): + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the response status is not 200. + """ + + credits_consumed: int + """The number of credits consumed by this request.""" + + credits_remaining: int + """The number of credits remaining for your organization after this request.""" + + class BrandWebScrapeSitemapResponse(BaseModel): domain: str """The normalized domain that was crawled""" @@ -38,3 +51,10 @@ class BrandWebScrapeSitemapResponse(BaseModel): urls: List[str] """Array of discovered page URLs from the sitemap (max 500)""" + + key_metadata: Optional[KeyMetadata] = None + """Metadata about the API key used for the request. + + Included in every response whenever a valid API key is provided, even when the + response status is not 200. + """ From 6ce83f3e0cfab22bfce26cbfd293e9bd27d308ca Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 11 Jun 2026 11:15:54 +0000 Subject: [PATCH 80/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 503623ef..1090a51e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-5545bb30aeac61ddb7d2b84ada399e9cd1cdc531a494b41e2ccf720056bdd437.yml -openapi_spec_hash: 554a88e7acd677dd0acac34b3900e5c6 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-3bfe60884f1e3f19275b10d8d6d7d5ce22d7cd7aff6847eb433d8a9b77c2ecc7.yml +openapi_spec_hash: d6bd2e085a73b0a5d6ddfa20d5949094 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From cfc6c971f8752d6ef8fe6861cf287bf5be65b783 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 23:16:15 +0000 Subject: [PATCH 81/82] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 1090a51e..8066585f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 16 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-3bfe60884f1e3f19275b10d8d6d7d5ce22d7cd7aff6847eb433d8a9b77c2ecc7.yml -openapi_spec_hash: d6bd2e085a73b0a5d6ddfa20d5949094 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev/brand.dev-8fc464c01db0ca1150918b073df5e6b3dc4983268a3f2db1ee07b66a16b0a5fc.yml +openapi_spec_hash: 53f4527ee9daaed3e8da72bdda5657b5 config_hash: 91cf2dcefb99c39eb9cd3e98e15d6011 From 5f20f440f4a62d66cf07c19f922ef8d0e145ac7d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 23:17:08 +0000 Subject: [PATCH 82/82] release: 1.44.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 59 +++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/brand/dev/_version.py | 2 +- 4 files changed, 62 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b5fcdb93..ba2c5854 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.43.0" + ".": "1.44.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d135655d..94ef9e46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,64 @@ # Changelog +## 1.44.0 (2026-06-16) + +Full Changelog: [v1.43.0...v1.44.0](https://github.com/context-dot-dev/deprecated-brand-python-sdk/compare/v1.43.0...v1.44.0) + +### Features + +* **api:** api update ([835e3dc](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/835e3dc7d16ab4d4d7d8e6223b7671103ea99316)) +* **api:** api update ([b35bfa8](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/b35bfa83453d3ee820e8aab7a3914545924f6df8)) +* **api:** api update ([b868536](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/b86853684cdddc71c11371f7ac065004bd415078)) +* **api:** api update ([4f2fb89](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/4f2fb892f17828300c268e9e316477b3ee9de0b4)) +* **api:** api update ([bb7be8c](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/bb7be8c9f17e5a86aa69e7651cac1ae7b3c170c2)) +* **api:** api update ([90b98b6](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/90b98b60f940e7aa127cb2b52fbdf0e467cd45c1)) +* **api:** api update ([44865c2](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/44865c2f5a63e7d0e2e4ccd0c4d2c06206044fe8)) +* **api:** api update ([291415a](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/291415a60a6ee87cc6b27364e6b71e039ccb3590)) +* **api:** api update ([ce906ee](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/ce906ee12160bff29e64d8c140decf63c02fa104)) +* **api:** api update ([00262df](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/00262dff8700b717cdacde8d05c040c2e240b94b)) +* **api:** api update ([fcd2959](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/fcd2959f013e42957386b17c540c3b69fde4b08e)) +* **api:** api update ([34da1c0](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/34da1c09d1e5883a6134fba8bef1a2622a841a3d)) +* **api:** api update ([5573ee7](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/5573ee7e42f5018a95e85e66460bfccc3d695a2a)) +* **api:** api update ([e4f1307](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/e4f1307a639a0c14467bd4c6af34eafe96b86033)) +* **api:** api update ([3e783d9](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/3e783d9ad471999f34ed2cbc92a58099f15ce9cb)) +* **api:** api update ([f9047c7](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/f9047c77841d0b1707cdeab18880d93dd2e6a115)) +* **api:** api update ([390e85c](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/390e85cad5c6199e1a3d03de72e77d54f0108234)) +* **api:** api update ([74f22a5](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/74f22a55a3d82b5adcc63d5f12218a162180ae95)) +* **api:** api update ([a04e938](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/a04e9389794725781408dc53d635226406d89cb2)) +* **api:** api update ([b4672ab](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/b4672ab67f11664113177977497c75e6c5ce82f8)) +* **api:** api update ([b494e66](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/b494e66bdee0f1c447ee587836aa74c7955da0aa)) +* **api:** api update ([af4c2fa](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/af4c2fa317c4a5cd2ef86e6562fe41ff7436daed)) +* **api:** api update ([9d4a64e](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/9d4a64e28b1321eda8463d8c86ec8a130c78c318)) +* **api:** api update ([f13cc2b](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/f13cc2be3ce7c3355168ba62bb3bd5ec5aaf4105)) +* **api:** api update ([93a9472](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/93a9472c495a1d34afa82ff7a6889e32ff706e3f)) +* **api:** api update ([bf48ceb](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/bf48ceb8693e734ab3da2b89e80f394d351215e6)) +* **api:** api update ([b5e8905](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/b5e8905b3e8554890bda8c7eb9e4255c363f1a81)) +* **api:** api update ([04fafa1](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/04fafa1b94a4047cc9fb3825ab84e6f517baefc4)) +* **api:** api update ([bd0f93f](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/bd0f93f4df85dc1e822ca6b59fb7ce594976ad49)) +* **api:** api update ([a388d39](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/a388d39c93b50ec075a553957081a45f64b73d39)) +* **api:** api update ([9213463](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/9213463afaf5d76d3b706a5aef49082c72409189)) +* **internal/types:** support eagerly validating pydantic iterators ([06b88f4](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/06b88f47143a8e1c9182ceab6bf3c1f2eca6cfdc)) +* support setting headers via env ([0ce56bb](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/0ce56bbd77bbb7170a90b838c9dff09616dea488)) + + +### Bug Fixes + +* **client:** add missing f-string prefix in file type error message ([af1c0ca](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/af1c0cab6151a11859136bd4cbbd8a0a200acdd5)) +* **client:** preserve hardcoded query params when merging with user params ([a92d7b7](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/a92d7b7c66f52b259cbf31d938c8a70565190b5c)) +* ensure file data are only sent as 1 parameter ([9b72a51](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/9b72a51afe0d9992f3c52ee727c1ffc1ec76312f)) +* use correct field name format for multipart file arrays ([c74c369](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/c74c3699de55fd58f298fa9a9d378db7f677a344)) + + +### Performance Improvements + +* **client:** optimize file structure copying in multipart requests ([d7e4a18](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/d7e4a181dd0e9146f98bb51bb977300eac439c6e)) + + +### Chores + +* **internal:** more robust bootstrap script ([28007d9](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/28007d91dbe45b20fc33670466006d9934ac7c5a)) +* **internal:** reformat pyproject.toml ([3e37dca](https://github.com/context-dot-dev/deprecated-brand-python-sdk/commit/3e37dcaafaafaa677893794e66cec86afedb65ca)) + ## 1.43.0 (2026-04-03) Full Changelog: [v1.42.0...v1.43.0](https://github.com/context-dot-dev/deprecated-brand-python-sdk/compare/v1.42.0...v1.43.0) diff --git a/pyproject.toml b/pyproject.toml index 4789c5ee..8f59f086 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "brand.dev" -version = "1.43.0" +version = "1.44.0" description = "The official Python library for the brand.dev API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/brand/dev/_version.py b/src/brand/dev/_version.py index 6c398c02..236a1892 100644 --- a/src/brand/dev/_version.py +++ b/src/brand/dev/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "brand.dev" -__version__ = "1.43.0" # x-release-please-version +__version__ = "1.44.0" # x-release-please-version