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 @@
{
".": "1.14.2"
".": "2.0.0"
}
8 changes: 4 additions & 4 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 84
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/coingecko%2Fcoingecko-e88de4e81cdb0547e40a951e747c95e7a9647577d8bee0ce5c5b4643a939ed3f.yml
openapi_spec_hash: d3b574a0f909b96fd93f1f46a2f8efde
config_hash: f101f417dba7f9352f7573639dd5938f
configured_endpoints: 85
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/coingecko/coingecko-705ab95ba3a2ad5b97b9a9a8d942a52f918da8b2544b1a156846dd438c1d26d4.yml
openapi_spec_hash: 82c4000d4dbeb57eabac6bb6cf9e3dc1
config_hash: c77352c905b1a7d1ab31960e5195bddf
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
# Changelog

## 2.0.0 (2026-04-30)

Full Changelog: [v1.14.2...v2.0.0](https://github.com/coingecko/coingecko-python/compare/v1.14.2...v2.0.0)

### ⚠ BREAKING CHANGES

* AIP updates; fix!: Pydantic response model

### Features

* AIP updates; fix!: Pydantic response model ([8c4629e](https://github.com/coingecko/coingecko-python/commit/8c4629e7c7431cc8925b757fd23c205816357ed6))
* support setting headers via env ([3979340](https://github.com/coingecko/coingecko-python/commit/3979340037d5ec65908c8c9e8d21d67e6e770ae8))


### Bug Fixes

* use correct field name format for multipart file arrays ([5932ae6](https://github.com/coingecko/coingecko-python/commit/5932ae6d020e9841a92773aee20ea926b849945e))


### Chores

* **internal:** more robust bootstrap script ([e53a0fc](https://github.com/coingecko/coingecko-python/commit/e53a0fc7939c399537fefd1cf9d77044a1925c94))

## 1.14.2 (2026-04-18)

Full Changelog: [v1.14.1...v1.14.2](https://github.com/coingecko/coingecko-python/compare/v1.14.1...v1.14.2)
Expand Down
17 changes: 15 additions & 2 deletions api.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Methods:
Types:

```python
from coingecko_sdk.types import CoinGetIDResponse
from coingecko_sdk.types import DetailPlatformData, CoinGetIDResponse
```

Methods:
Expand Down Expand Up @@ -317,6 +317,18 @@ Methods:

- <code title="get /key">client.key.<a href="./src/coingecko_sdk/resources/key.py">get</a>() -> <a href="./src/coingecko_sdk/types/key_get_response.py">KeyGetResponse</a></code>

# News

Types:

```python
from coingecko_sdk.types import NewsGetResponse
```

Methods:

- <code title="get /news">client.news.<a href="./src/coingecko_sdk/resources/news.py">get</a>(\*\*<a href="src/coingecko_sdk/types/news_get_params.py">params</a>) -> <a href="./src/coingecko_sdk/types/news_get_response.py">NewsGetResponse</a></code>

# NFTs

Types:
Expand Down Expand Up @@ -453,7 +465,7 @@ Methods:
Types:

```python
from coingecko_sdk.types.onchain.networks import PoolGetResponse, PoolGetAddressResponse
from coingecko_sdk.types.onchain.networks import PoolData, PoolGetResponse, PoolGetAddressResponse
```

Methods:
Expand Down Expand Up @@ -705,6 +717,7 @@ Types:

```python
from coingecko_sdk.types import (
TreasuryEntity,
PublicTreasuryGetCoinIDResponse,
PublicTreasuryGetEntityIDResponse,
PublicTreasuryGetHoldingChartResponse,
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 = "coingecko_sdk"
version = "1.14.2"
version = "2.0.0"
description = "The official Python library for the coingecko API"
dynamic = ["readme"]
license = "Apache-2.0"
Expand Down
2 changes: 1 addition & 1 deletion scripts/bootstrap
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
62 changes: 61 additions & 1 deletion src/coingecko_sdk/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,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
Expand All @@ -34,6 +38,7 @@
if TYPE_CHECKING:
from .resources import (
key,
news,
nfts,
ping,
coins,
Expand All @@ -50,6 +55,7 @@
public_treasury,
)
from .resources.key import KeyResource, AsyncKeyResource
from .resources.news import NewsResource, AsyncNewsResource
from .resources.ping import PingResource, AsyncPingResource
from .resources.entities import EntitiesResource, AsyncEntitiesResource
from .resources.nfts.nfts import NFTsResource, AsyncNFTsResource
Expand Down Expand Up @@ -155,6 +161,15 @@ def __init__(
except KeyError as exc:
raise ValueError(f"Unknown environment: {environment}") from exc

custom_headers_env = os.environ.get("COINGECKO_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,
Expand Down Expand Up @@ -214,6 +229,12 @@ def key(self) -> KeyResource:

return KeyResource(self)

@cached_property
def news(self) -> NewsResource:
from .resources.news import NewsResource

return NewsResource(self)

@cached_property
def nfts(self) -> NFTsResource:
from .resources.nfts import NFTsResource
Expand Down Expand Up @@ -470,6 +491,15 @@ def __init__(
except KeyError as exc:
raise ValueError(f"Unknown environment: {environment}") from exc

custom_headers_env = os.environ.get("COINGECKO_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,
Expand Down Expand Up @@ -529,6 +559,12 @@ def key(self) -> AsyncKeyResource:

return AsyncKeyResource(self)

@cached_property
def news(self) -> AsyncNewsResource:
from .resources.news import AsyncNewsResource

return AsyncNewsResource(self)

@cached_property
def nfts(self) -> AsyncNFTsResource:
from .resources.nfts import AsyncNFTsResource
Expand Down Expand Up @@ -767,6 +803,12 @@ def key(self) -> key.KeyResourceWithRawResponse:

return KeyResourceWithRawResponse(self._client.key)

@cached_property
def news(self) -> news.NewsResourceWithRawResponse:
from .resources.news import NewsResourceWithRawResponse

return NewsResourceWithRawResponse(self._client.news)

@cached_property
def nfts(self) -> nfts.NFTsResourceWithRawResponse:
from .resources.nfts import NFTsResourceWithRawResponse
Expand Down Expand Up @@ -864,6 +906,12 @@ def key(self) -> key.AsyncKeyResourceWithRawResponse:

return AsyncKeyResourceWithRawResponse(self._client.key)

@cached_property
def news(self) -> news.AsyncNewsResourceWithRawResponse:
from .resources.news import AsyncNewsResourceWithRawResponse

return AsyncNewsResourceWithRawResponse(self._client.news)

@cached_property
def nfts(self) -> nfts.AsyncNFTsResourceWithRawResponse:
from .resources.nfts import AsyncNFTsResourceWithRawResponse
Expand Down Expand Up @@ -961,6 +1009,12 @@ def key(self) -> key.KeyResourceWithStreamingResponse:

return KeyResourceWithStreamingResponse(self._client.key)

@cached_property
def news(self) -> news.NewsResourceWithStreamingResponse:
from .resources.news import NewsResourceWithStreamingResponse

return NewsResourceWithStreamingResponse(self._client.news)

@cached_property
def nfts(self) -> nfts.NFTsResourceWithStreamingResponse:
from .resources.nfts import NFTsResourceWithStreamingResponse
Expand Down Expand Up @@ -1058,6 +1112,12 @@ def key(self) -> key.AsyncKeyResourceWithStreamingResponse:

return AsyncKeyResourceWithStreamingResponse(self._client.key)

@cached_property
def news(self) -> news.AsyncNewsResourceWithStreamingResponse:
from .resources.news import AsyncNewsResourceWithStreamingResponse

return AsyncNewsResourceWithStreamingResponse(self._client.news)

@cached_property
def nfts(self) -> nfts.AsyncNFTsResourceWithStreamingResponse:
from .resources.nfts import AsyncNFTsResourceWithStreamingResponse
Expand Down
8 changes: 2 additions & 6 deletions src/coingecko_sdk/_qs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/coingecko_sdk/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 34 additions & 8 deletions src/coingecko_sdk/_utils/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, ...])
Expand All @@ -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', '<array>', 'data'].

``array_format`` controls how ``<array>`` 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]
Expand All @@ -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)
Expand Down Expand Up @@ -106,6 +128,7 @@ def _extract_items(
path,
index=index,
flattened_key=flattened_key,
array_format=array_format,
)
elif is_list(obj):
if key != "<array>":
Expand All @@ -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)
]
)

Expand Down
2 changes: 1 addition & 1 deletion src/coingecko_sdk/_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__ = "coingecko_sdk"
__version__ = "1.14.2" # x-release-please-version
__version__ = "2.0.0" # x-release-please-version
Loading