Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.43.0"
".": "0.44.0"
}
6 changes: 3 additions & 3 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 42
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/profound/profound-4fb090ac5b0517a401c403e173daa5ca2315cb6a39c59dd301fce91e9ec4b7e8.yml
openapi_spec_hash: 7a2fa5d5d32944baa3cf81400251b900
config_hash: fd78680283b85cba4aa8ddb88c757308
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/profound/profound-a4fb4c2b593d32e808cc7d918c9406a50f67b0c71eb509120b94379de5c3a8cc.yml
openapi_spec_hash: d8899b45e2fee59974c91a793814a9ec
config_hash: 511c02edba705a2d6e2b71bbd6051506
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## 0.44.0 (2026-05-04)

Full Changelog: [v0.43.0...v0.44.0](https://github.com/cooper-square-technologies/profound-python-sdk/compare/v0.43.0...v0.44.0)

### Features

* **api:** api update ([70d45e3](https://github.com/cooper-square-technologies/profound-python-sdk/commit/70d45e37d2f49272ef71664ec2511a5de4a5612f))
* **api:** api update ([1bf69d1](https://github.com/cooper-square-technologies/profound-python-sdk/commit/1bf69d1792cdd1963fd2baeb07ae5723767b9c77))
* **api:** api update ([efcf127](https://github.com/cooper-square-technologies/profound-python-sdk/commit/efcf1277e29af8839fb53497a9a81bec409a06d6))
* **api:** api update ([878aedf](https://github.com/cooper-square-technologies/profound-python-sdk/commit/878aedff119fe1068f2f7c1c8ff0230680b855e1))
* **api:** configure `array_format` ([6d31755](https://github.com/cooper-square-technologies/profound-python-sdk/commit/6d3175599773bc6b99a220aa5caa8bb414644e48))
* **api:** dual auth yml ([b9fbf14](https://github.com/cooper-square-technologies/profound-python-sdk/commit/b9fbf1498c2829ef5c7bf00d7c13b8aa20511c85))
* **api:** update package name mcp - rollback package manager to yarn ([27b50ba](https://github.com/cooper-square-technologies/profound-python-sdk/commit/27b50ba580dba6b8d8ed86a0f7c96efe308d37ea))

## 0.43.0 (2026-05-01)

Full Changelog: [v0.42.0...v0.43.0](https://github.com/cooper-square-technologies/profound-python-sdk/compare/v0.42.0...v0.43.0)
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ and offers both synchronous and asynchronous clients powered by [httpx](https://

Use the Profound MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.

[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=%40profoundai%2Fmcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBwcm9mb3VuZGFpL21jcCJdLCJlbnYiOnsiUFJPRk9VTkRfQVBJX0tFWSI6Ik15IEFQSSBLZXkifX0)
[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40profoundai%2Fmcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40profoundai%2Fmcp%22%5D%2C%22env%22%3A%7B%22PROFOUND_API_KEY%22%3A%22My%20API%20Key%22%7D%7D)
[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=%40profoundai%2Fmcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBwcm9mb3VuZGFpL21jcCJdLCJlbnYiOnsiUFJPRk9VTkRfQUNDRVNTX1RPS0VOIjoiTXkgQWNjZXNzIFRva2VuIiwiUFJPRk9VTkRfQVBJX0tFWSI6Ik15IEFQSSBLZXkifX0)
[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40profoundai%2Fmcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40profoundai%2Fmcp%22%5D%2C%22env%22%3A%7B%22PROFOUND_ACCESS_TOKEN%22%3A%22My%20Access%20Token%22%2C%22PROFOUND_API_KEY%22%3A%22My%20API%20Key%22%7D%7D)

> Note: You may need to set environment variables in your MCP client.

Expand Down Expand Up @@ -42,10 +42,10 @@ client = Profound(
categories = client.organizations.categories.list()
```

While you can provide an `api_key` keyword argument,
While you can provide a `access_token` keyword argument,
we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/)
to add `PROFOUND_API_KEY="My API Key"` to your `.env` file
so that your API Key is not stored in source control.
to add `PROFOUND_ACCESS_TOKEN="My Access Token"` to your `.env` file
so that your Access Token is not stored in source control.

## Async usage

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "profound"
version = "0.43.0"
version = "0.44.0"
description = "The official Python library for the profound API"
dynamic = ["readme"]
license = "Apache-2.0"
Expand Down
93 changes: 78 additions & 15 deletions src/profound/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from ._qs import Querystring
from ._types import (
Omit,
Headers,
Timeout,
NotGiven,
Transport,
Expand All @@ -27,7 +28,7 @@
from ._compat import cached_property
from ._version import __version__
from ._streaming import Stream as Stream, AsyncStream as AsyncStream
from ._exceptions import ProfoundError, APIStatusError
from ._exceptions import APIStatusError
from ._base_client import (
DEFAULT_MAX_RETRIES,
SyncAPIClient,
Expand Down Expand Up @@ -58,11 +59,13 @@

class Profound(SyncAPIClient):
# client options
api_key: str
access_token: str | None
api_key: str | None

def __init__(
self,
*,
access_token: str | None = None,
api_key: str | None = None,
base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
Expand All @@ -85,14 +88,16 @@ def __init__(
) -> None:
"""Construct a new synchronous Profound client instance.

This automatically infers the `api_key` argument from the `PROFOUND_API_KEY` environment variable if it is not provided.
This automatically infers the following arguments from their corresponding environment variables if they are not provided:
- `access_token` from `PROFOUND_ACCESS_TOKEN`
- `api_key` from `PROFOUND_API_KEY`
"""
if access_token is None:
access_token = os.environ.get("PROFOUND_ACCESS_TOKEN")
self.access_token = access_token

if api_key is None:
api_key = os.environ.get("PROFOUND_API_KEY")
if api_key is None:
raise ProfoundError(
"The api_key client option must be set either by passing api_key to the client or by setting the PROFOUND_API_KEY environment variable"
)
self.api_key = api_key

if base_url is None:
Expand Down Expand Up @@ -173,12 +178,25 @@ def with_streaming_response(self) -> ProfoundWithStreamedResponse:
@property
@override
def qs(self) -> Querystring:
return Querystring(array_format="comma")
return Querystring(array_format="repeat")

@property
@override
def auth_headers(self) -> dict[str, str]:
return {**self._bearer_auth, **self._api_key_header}

@property
def _bearer_auth(self) -> dict[str, str]:
access_token = self.access_token
if access_token is None:
return {}
return {"Authorization": f"Bearer {access_token}"}

@property
def _api_key_header(self) -> dict[str, str]:
api_key = self.api_key
if api_key is None:
return {}
return {"X-API-Key": api_key}

@property
Expand All @@ -190,9 +208,22 @@ def default_headers(self) -> dict[str, str | Omit]:
**self._custom_headers,
}

@override
def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit):
return

if headers.get("X-API-Key") or isinstance(custom_headers.get("X-API-Key"), Omit):
return

raise TypeError(
'"Could not resolve authentication method. Expected either access_token or api_key to be set. Or for one of the `Authorization` or `X-API-Key` headers to be explicitly omitted"'
)

def copy(
self,
*,
access_token: str | None = None,
api_key: str | None = None,
base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
Expand Down Expand Up @@ -227,6 +258,7 @@ def copy(

http_client = http_client or self._client
return self.__class__(
access_token=access_token or self.access_token,
api_key=api_key or self.api_key,
base_url=base_url or self.base_url,
timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
Expand Down Expand Up @@ -277,11 +309,13 @@ def _make_status_error(

class AsyncProfound(AsyncAPIClient):
# client options
api_key: str
access_token: str | None
api_key: str | None

def __init__(
self,
*,
access_token: str | None = None,
api_key: str | None = None,
base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
Expand All @@ -304,14 +338,16 @@ def __init__(
) -> None:
"""Construct a new async AsyncProfound client instance.

This automatically infers the `api_key` argument from the `PROFOUND_API_KEY` environment variable if it is not provided.
This automatically infers the following arguments from their corresponding environment variables if they are not provided:
- `access_token` from `PROFOUND_ACCESS_TOKEN`
- `api_key` from `PROFOUND_API_KEY`
"""
if access_token is None:
access_token = os.environ.get("PROFOUND_ACCESS_TOKEN")
self.access_token = access_token

if api_key is None:
api_key = os.environ.get("PROFOUND_API_KEY")
if api_key is None:
raise ProfoundError(
"The api_key client option must be set either by passing api_key to the client or by setting the PROFOUND_API_KEY environment variable"
)
self.api_key = api_key

if base_url is None:
Expand Down Expand Up @@ -392,12 +428,25 @@ def with_streaming_response(self) -> AsyncProfoundWithStreamedResponse:
@property
@override
def qs(self) -> Querystring:
return Querystring(array_format="comma")
return Querystring(array_format="repeat")

@property
@override
def auth_headers(self) -> dict[str, str]:
return {**self._bearer_auth, **self._api_key_header}

@property
def _bearer_auth(self) -> dict[str, str]:
access_token = self.access_token
if access_token is None:
return {}
return {"Authorization": f"Bearer {access_token}"}

@property
def _api_key_header(self) -> dict[str, str]:
api_key = self.api_key
if api_key is None:
return {}
return {"X-API-Key": api_key}

@property
Expand All @@ -409,9 +458,22 @@ def default_headers(self) -> dict[str, str | Omit]:
**self._custom_headers,
}

@override
def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit):
return

if headers.get("X-API-Key") or isinstance(custom_headers.get("X-API-Key"), Omit):
return

raise TypeError(
'"Could not resolve authentication method. Expected either access_token or api_key to be set. Or for one of the `Authorization` or `X-API-Key` headers to be explicitly omitted"'
)

def copy(
self,
*,
access_token: str | None = None,
api_key: str | None = None,
base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
Expand Down Expand Up @@ -446,6 +508,7 @@ def copy(

http_client = http_client or self._client
return self.__class__(
access_token=access_token or self.access_token,
api_key=api_key or self.api_key,
base_url=base_url or self.base_url,
timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
Expand Down
2 changes: 1 addition & 1 deletion src/profound/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

__title__ = "profound"
__version__ = "0.43.0" # x-release-please-version
__version__ = "0.44.0" # x-release-please-version
4 changes: 2 additions & 2 deletions src/profound/resources/organizations/categories.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ def prompts(
self,
category_id: str,
*,
analysis_type: List[Literal["visibility", "sentiment", "accuracy"]] | Omit = omit,
analysis_type: List[Literal["visibility", "sentiment", "sentiment_v2", "accuracy"]] | Omit = omit,
cursor: Optional[str] | Omit = omit,
limit: int | Omit = omit,
order_dir: Literal["asc", "desc"] | Omit = omit,
Expand Down Expand Up @@ -656,7 +656,7 @@ async def prompts(
self,
category_id: str,
*,
analysis_type: List[Literal["visibility", "sentiment", "accuracy"]] | Omit = omit,
analysis_type: List[Literal["visibility", "sentiment", "sentiment_v2", "accuracy"]] | Omit = omit,
cursor: Optional[str] | Omit = omit,
limit: int | Omit = omit,
order_dir: Literal["asc", "desc"] | Omit = omit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Prompt(TypedDict, total=False):
topic: Required[IDOrNameParam]
"""Topic to assign. A new topic is created if the name doesn't exist."""

analysis_types: Optional[List[Literal["visibility", "sentiment", "accuracy"]]]
analysis_types: Optional[List[Literal["visibility", "sentiment", "sentiment_v2", "accuracy"]]]
"""Analysis types: 'visibility', 'sentiment', 'accuracy'.

Defaults to ['visibility'].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Prompt(BaseModel):
topic: NamedResource
"""Resolved topic."""

analysis_types: Optional[List[Literal["visibility", "sentiment", "accuracy"]]] = None
analysis_types: Optional[List[Literal["visibility", "sentiment", "sentiment_v2", "accuracy"]]] = None
"""Analysis types assigned to this prompt."""

asset: Optional[NamedResource] = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@


class CategoryPromptsParams(TypedDict, total=False):
analysis_type: List[Literal["visibility", "sentiment", "accuracy"]]
analysis_type: List[Literal["visibility", "sentiment", "sentiment_v2", "accuracy"]]
"""Filter by analysis type (visibility, sentiment, accuracy)."""

cursor: Optional[str]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class Data(BaseModel):

updated_at: datetime

analysis_types: Optional[List[Literal["visibility", "sentiment", "accuracy"]]] = None
analysis_types: Optional[List[Literal["visibility", "sentiment", "sentiment_v2", "accuracy"]]] = None

personas: Optional[List[NamedResource]] = None

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class Prompt(TypedDict, total=False):
id: Required[str]
"""ID of the prompt to update."""

analysis_types: Optional[List[Literal["visibility", "sentiment", "accuracy"]]]
analysis_types: Optional[List[Literal["visibility", "sentiment", "sentiment_v2", "accuracy"]]]
"""New analysis types. Replaces all existing analysis types on the prompt."""

asset: Optional[IDOrNameParam]
Expand Down
5 changes: 4 additions & 1 deletion src/profound/types/shared/analysis_type_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@ class AnalysisTypeFilter(BaseModel):
"not_contains_case_insensitive",
]

value: Union[Literal["visibility", "sentiment", "accuracy"], List[Literal["visibility", "sentiment", "accuracy"]]]
value: Union[
Literal["visibility", "sentiment", "sentiment_v2", "accuracy"],
List[Literal["visibility", "sentiment", "sentiment_v2", "accuracy"]],
]
20 changes: 20 additions & 0 deletions src/profound/types/shared/bot_name_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ class BotNameFilter(BaseModel):
"Perplexity-User",
"Grok-PageBrowser",
"YouBot",
"OpenClaw",
"baiduspider",
"CCBot",
"ERNIEBot",
"Gemini-Fetch",
"YandexBot",
"PetalBot",
"MistralAI-User",
"Slurp",
"Gemini-Deep-Research",
],
List[
Literal[
Expand Down Expand Up @@ -88,6 +98,16 @@ class BotNameFilter(BaseModel):
"Perplexity-User",
"Grok-PageBrowser",
"YouBot",
"OpenClaw",
"baiduspider",
"CCBot",
"ERNIEBot",
"Gemini-Fetch",
"YandexBot",
"PetalBot",
"MistralAI-User",
"Slurp",
"Gemini-Deep-Research",
]
],
]
Loading
Loading