├── .gitignore
├── CONTRIBUTING.md
├── HISTORY.md
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.md
├── docs
├── .readthedocs.yaml
├── Makefile
├── conf.py
├── contributing.md
├── history.md
├── index.md
├── readme.md
├── requirements.txt
└── usage-examples.md
├── opensea
├── __init__.py
├── opensea.py
├── opensea_api.py
└── utils.py
├── requirements_dev.txt
├── setup.cfg
├── setup.py
├── tests
└── __init__.py
└── tox.ini
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 |
4 | # Distribution / packaging
5 | .Python
6 | build/
7 | develop-eggs/
8 | dist/
9 | downloads/
10 | eggs/
11 | .eggs/
12 | lib/
13 | lib64/
14 | parts/
15 | sdist/
16 | var/
17 | wheels/
18 | pip-wheel-metadata/
19 | share/python-wheels/
20 | *.egg-info/
21 | .installed.cfg
22 | *.egg
23 | MANIFEST
24 |
25 | # Installer logs
26 | pip-log.txt
27 | pip-delete-this-directory.txt
28 |
29 | # Sphinx documentation
30 | docs/_build/
31 |
32 | # Environments
33 | .env
34 | .venv
35 | env/
36 | venv/
37 | ENV/
38 |
39 | # pytest
40 | .pytest_cache
41 |
42 |
43 | .pypirc
44 | .vscode/
45 | .tox/
46 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | * [Submit an issue](https://github.com/zseta/python-opensea/issues) to report a bug or suggest a new feature
4 | * Submit a PR with a bugfix or a new feature.
5 | * Write a blog post or a tutorial about how you are using the OpenSea API and this wrapper
--------------------------------------------------------------------------------
/HISTORY.md:
--------------------------------------------------------------------------------
1 | # History
2 |
3 | ## 0.1.8 (2022-xx-xx)
4 | * Add `include_orders` in the assets api
5 | * Add cursor-based pagination in `assets` endpoint
6 |
7 | ## 0.1.7 (2022-03-26)
8 | * Add support for [asset listings](https://docs.opensea.io/reference/asset-listings)
9 | and [asset offers](https://docs.opensea.io/reference/asset-offers) endpoints
10 | * Add `occured_after` and `collection_editor` arguments to events endpoint
11 | * Handle SSL error when making requests
12 | * Docs: add example to paginate the events endpoint (using `events_backfill()`)
13 |
14 | ## 0.1.6 (2022-02-25)
15 | * Fix /events endpoint pagination (`events_backfill()` function) by
16 | passing only *the cursor hash* and not the full URL to the next request.
17 |
18 | ## 0.1.5 (2022-02-17)
19 |
20 | * Ability to override base_url with any other URL
21 | * Support for cursor-based pagination for /events endpoint (and removed deprecated arguments)
22 | * New function to help paginate the /events endpoint
23 | * Introducing a temporary function to fix the `next` url problem until OpenSea addresses this issue
24 | * Minor docs updates and cleanup
25 |
26 |
27 | ## 0.1.3 (2021-12-03)
28 |
29 | * Ability to reach all endpoints from one `OpenseaAPI` object
30 | * API key support (Opensea requires it from now on)
31 |
32 | ## 0.1.0 (2021-11-07)
33 |
34 | * First release on PyPI.
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021, Attila Tóth
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include CONTRIBUTING.md
2 | include HISTORY.md
3 | include LICENSE
4 | include README.md
5 |
6 | recursive-exclude * __pycache__
7 | recursive-exclude * *.py[co]
8 | recursive-exclude tests *
9 |
10 | recursive-include docs *.md conf.py Makefile make.bat *.jpg *.png *.gif
11 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: clean clean-test clean-pyc clean-build docs help
2 | .DEFAULT_GOAL := help
3 |
4 | define BROWSER_PYSCRIPT
5 | import os, webbrowser, sys
6 |
7 | from urllib.request import pathname2url
8 |
9 | webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1])))
10 | endef
11 | export BROWSER_PYSCRIPT
12 |
13 | define PRINT_HELP_PYSCRIPT
14 | import re, sys
15 |
16 | for line in sys.stdin:
17 | match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line)
18 | if match:
19 | target, help = match.groups()
20 | print("%-20s %s" % (target, help))
21 | endef
22 | export PRINT_HELP_PYSCRIPT
23 |
24 | BROWSER := python -c "$$BROWSER_PYSCRIPT"
25 |
26 | help:
27 | @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST)
28 |
29 | clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts
30 |
31 | clean-build: ## remove build artifacts
32 | rm -fr build/
33 | rm -fr dist/
34 | rm -fr .eggs/
35 | find . -name '*.egg-info' -exec rm -fr {} +
36 | find . -name '*.egg' -exec rm -f {} +
37 |
38 | clean-pyc: ## remove Python file artifacts
39 | find . -name '*.pyc' -exec rm -f {} +
40 | find . -name '*.pyo' -exec rm -f {} +
41 | find . -name '*~' -exec rm -f {} +
42 | find . -name '__pycache__' -exec rm -fr {} +
43 |
44 | clean-test: ## remove test and coverage artifacts
45 | rm -fr .tox/
46 | rm -f .coverage
47 | rm -fr htmlcov/
48 | rm -fr .pytest_cache
49 |
50 | lint/flake8: ## check style with flake8
51 | flake8 opensea tests
52 | lint/black: ## check style with black
53 | black --check opensea tests
54 |
55 | lint: lint/flake8 lint/black ## check style
56 |
57 | test: ## run tests quickly with the default Python
58 | pytest
59 |
60 | test-all: ## run tests on every Python version with tox
61 | tox
62 |
63 | coverage: ## check code coverage quickly with the default Python
64 | coverage run --source opensea -m pytest
65 | coverage report -m
66 | coverage html
67 | $(BROWSER) htmlcov/index.html
68 |
69 | docs: ## generate Sphinx HTML documentation, including API docs
70 | rm -f docs/opensea.md
71 | rm -f docs/modules.md
72 | sphinx-apidoc -o docs/ opensea
73 | $(MAKE) -C docs clean
74 | $(MAKE) -C docs html
75 | $(BROWSER) docs/_build/html/index.html
76 |
77 | servedocs: docs ## compile the docs watching for changes
78 | watchmedo shell-command -p '*.md' -c '$(MAKE) -C docs html' -R -D .
79 |
80 | release: dist ## package and upload a release
81 | twine upload dist/*
82 |
83 | dist: clean ## builds source and wheel package
84 | python setup.py sdist
85 | python setup.py bdist_wheel
86 | ls -l dist
87 |
88 | install: clean ## install the package to the active Python's site-packages
89 | python setup.py install
90 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OpenSea NFT API Python 3 wrapper
2 | This an API wrapper library for the [OpenSea API](https://docs.opensea.io/reference/api-overview) written in Python 3.
3 |
4 | The library provides a simplified interface to fetch a diverse set of NFT data points from OpenSea.
5 |
6 | ## Supported endpoints
7 | The wrapper covers the following OpenSea API endpoints:
8 |
9 | * Single asset ([/asset](https://docs.opensea.io/reference/retrieving-a-single-asset))
10 | * Single asset contract ([/asset_contract](https://docs.opensea.io/reference/retrieving-a-single-contract))
11 | * Single collection ([/collection](https://docs.opensea.io/reference/retrieving-a-single-collection))
12 | * Collection stats ([/collection/{slug}/stats](https://docs.opensea.io/reference/retrieving-collection-stats))
13 | * Multiple assets] ([/assets](https://docs.opensea.io/reference/getting-assets))
14 | * Multiple collections ([/collections](https://docs.opensea.io/reference/retrieving-collections))
15 | * Multiple events ([/events](https://docs.opensea.io/reference/retrieving-asset-events))
16 | * Multiple bundles ([/bundles](https://docs.opensea.io/reference/retrieving-bundles))
17 | * Asset listings ([/asset/{asset_contract_address}/{token_id}/listings](https://docs.opensea.io/reference/asset-listings))
18 | * Asset offers ([/asset/{asset_contract_address}/{token_id}/offers](https://docs.opensea.io/reference/asset-offers))
19 |
20 |
21 | ## Prerequisite
22 | You need to have an **API key** to use the OpenSea API, and thus
23 | you need one to use this wrapper too. [You can request a key here.](https://docs.opensea.io/reference/request-an-api-key)
24 | NOTE: The API key can take over 4-7 days to be delivered. It also requires you to show the project you are working on.
25 |
26 | ## Installation
27 | Install with pip:
28 | ```bash
29 | virtualenv env && source env/bin/activate
30 | pip install opensea-api
31 | ```
32 |
33 | ### Upgrade
34 | ```bash
35 | pip install opensea-api -U
36 | ```
37 |
38 | ## Usage examples
39 |
40 | ```python
41 | # import the OpenseaAPI object from the opensea module
42 | from opensea import OpenseaAPI
43 |
44 | # create an object to interact with the Opensea API (need an api key)
45 | api = OpenseaAPI(apikey="YOUR APIKEY")
46 |
47 | # fetch a single asset
48 | contract_address = "0x495f947276749Ce646f68AC8c248420045cb7b5e"
49 | token_id = "66406747123743156841746366950152533278033835913591691491127082341586364792833"
50 | result = api.asset(asset_contract_address=contract_address, token_id=token_id)
51 |
52 | # fetch multiple assets
53 | result = api.assets(owner="0xce90a7949bb78892f159f428d0dc23a8e3584d75", limit=3)
54 |
55 | # fetch a single contract
56 | result = api.contract(asset_contract_address="0x495f947276749Ce646f68AC8c248420045cb7b5e")
57 |
58 | # fetch a single collection
59 | result = api.collection(collection_slug="cryptopunks")
60 |
61 | # fetch multiple collections
62 | result = api.collections(asset_owner="0xce90a7949bb78892f159f428d0dc23a8e3584d75", limit=3)
63 |
64 | # fetch collection stats
65 | result = api.collection_stats(collection_slug="cryptopunks")
66 |
67 | # fetch multiple events
68 | from opensea import utils as opensea_utils
69 |
70 | period_end = opensea_utils.datetime_utc(2021, 11, 6, 14, 30)
71 | result = api.events(
72 | occurred_before=period_end,
73 | limit=10,
74 | export_file_name="events.json",
75 | )
76 |
77 | # fetch multiple bundles
78 | result = api.bundles(limit=2)
79 |
80 | # paginate the events endpoint (cursors are handled internally)
81 | from datetime import datetime, timezone
82 |
83 | start_at = datetime(2021, 10, 5, 3, 25, tzinfo=timezone.utc)
84 | finish_at = datetime(2021, 10, 5, 3, 20, tzinfo=timezone.utc)
85 |
86 | event_generator = api.events_backfill(start=start_at,
87 | until=finish_at,
88 | event_type="successful")
89 | for event in event_generator:
90 | if event is not None:
91 | print(event) # or do other things with the event data
92 | ```
93 |
94 | [Here's a demo video showcasing the basics.](https://www.youtube.com/watch?v=ga4hTqNRjfw)
95 |
96 | ## Documentation
97 | * [Wrapper documentation](https://opensea-api.attilatoth.dev)
98 | * [OpenSea API documentation](https://docs.opensea.io/reference/api-overview)
99 |
100 |
--------------------------------------------------------------------------------
/docs/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | # .readthedocs.yaml
2 | # Read the Docs configuration file
3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4 |
5 | # Required
6 | version: 2
7 |
8 | # Build documentation in the docs/ directory with Sphinx
9 | sphinx:
10 | configuration: docs/conf.py
11 |
12 | # Optionally build your docs in additional formats such as PDF
13 | formats: all
14 |
15 | # Optionally set the version of Python and requirements required to build your docs
16 | python:
17 | version: "3.8"
18 | install:
19 | - requirements: docs/requirements.txt
20 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = python -msphinx
7 | SPHINXPROJ = opensea
8 | SOURCEDIR = .
9 | BUILDDIR = _build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # opensea documentation build configuration file, created by
4 | # sphinx-quickstart on Fri Jun 9 13:47:02 2017.
5 | #
6 | # This file is execfile()d with the current directory set to its
7 | # containing dir.
8 | #
9 | # Note that not all possible configuration values are present in this
10 | # autogenerated file.
11 | #
12 | # All configuration values have a default; values that are commented out
13 | # serve to show the default.
14 |
15 | # If extensions (or modules to document with autodoc) are in another
16 | # directory, add these directories to sys.path here. If the directory is
17 | # relative to the documentation root, use os.path.abspath to make it
18 | # absolute, like shown here.
19 | #
20 | import os
21 | import sys
22 | sys.path.insert(0, os.path.abspath('..'))
23 |
24 | import opensea
25 |
26 | # -- General configuration ---------------------------------------------
27 |
28 | # If your documentation needs a minimal Sphinx version, state it here.
29 | #
30 | # needs_sphinx = '1.0'
31 |
32 | # Add any Sphinx extension module names here, as strings. They can be
33 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
34 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'myst_parser']
35 |
36 | # Add any paths that contain templates here, relative to this directory.
37 | templates_path = ['_templates']
38 |
39 | # The suffix(es) of source filenames.
40 | # You can specify multiple suffix as a list of string:
41 | #
42 | # source_suffix = ['.rst', '.md']
43 | source_suffix = ['.md']
44 |
45 | # The master toctree document.
46 | master_doc = 'index'
47 |
48 | # General information about the project.
49 | project = 'OpenSea API Python wrapper'
50 | copyright = "2021, Attila Tóth"
51 | author = "Attila Tóth"
52 |
53 | # The version info for the project you're documenting, acts as replacement
54 | # for |version| and |release|, also used in various other places throughout
55 | # the built documents.
56 | #
57 | # The short X.Y version.
58 | version = opensea.__version__
59 | # The full version, including alpha/beta/rc tags.
60 | release = opensea.__version__
61 |
62 | # The language for content autogenerated by Sphinx. Refer to documentation
63 | # for a list of supported languages.
64 | #
65 | # This is also used if you do content translation via gettext catalogs.
66 | # Usually you set "language" from the command line for these cases.
67 | language = None
68 |
69 | # List of patterns, relative to source directory, that match files and
70 | # directories to ignore when looking for source files.
71 | # This patterns also effect to html_static_path and html_extra_path
72 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
73 |
74 | # The name of the Pygments (syntax highlighting) style to use.
75 | pygments_style = 'sphinx'
76 |
77 | # If true, `todo` and `todoList` produce output, else they produce nothing.
78 | todo_include_todos = False
79 |
80 |
81 | # -- Options for HTML output -------------------------------------------
82 |
83 | # The theme to use for HTML and HTML Help pages. See the documentation for
84 | # a list of builtin themes.
85 | #
86 | html_theme = 'sphinx_rtd_theme'
87 |
88 | # Theme options are theme-specific and customize the look and feel of a
89 | # theme further. For a list of options available for each theme, see the
90 | # documentation.
91 | #
92 | # html_theme_options = {}
93 |
94 | # Add any paths that contain custom static files (such as style sheets) here,
95 | # relative to this directory. They are copied after the builtin static files,
96 | # so a file named "default.css" will overwrite the builtin "default.css".
97 | html_static_path = ['_static']
98 |
99 |
100 | # -- Options for HTMLHelp output ---------------------------------------
101 |
102 | # Output file base name for HTML help builder.
103 | htmlhelp_basename = 'openseadoc'
104 |
105 |
106 | # -- Options for LaTeX output ------------------------------------------
107 |
108 | latex_elements = {
109 | # The paper size ('letterpaper' or 'a4paper').
110 | #
111 | # 'papersize': 'letterpaper',
112 |
113 | # The font size ('10pt', '11pt' or '12pt').
114 | #
115 | # 'pointsize': '10pt',
116 |
117 | # Additional stuff for the LaTeX preamble.
118 | #
119 | # 'preamble': '',
120 |
121 | # Latex figure (float) alignment
122 | #
123 | # 'figure_align': 'htbp',
124 | }
125 |
126 | # Grouping the document tree into LaTeX files. List of tuples
127 | # (source start file, target name, title, author, documentclass
128 | # [howto, manual, or own class]).
129 | latex_documents = [
130 | (master_doc, 'opensea.tex',
131 | 'OpenSea API Python wrapper documentation',
132 | 'Attila Toth', 'manual'),
133 | ]
134 |
135 |
136 | # -- Options for manual page output ------------------------------------
137 |
138 | # One entry per manual page. List of tuples
139 | # (source start file, name, description, authors, manual section).
140 | man_pages = [
141 | (master_doc, 'opensea',
142 | 'OpenSea API Python wrapper documentation',
143 | [author], 1)
144 | ]
145 |
146 |
147 | # -- Options for Texinfo output ----------------------------------------
148 |
149 | # Grouping the document tree into Texinfo files. List of tuples
150 | # (source start file, target name, title, author,
151 | # dir menu entry, description, category)
152 | texinfo_documents = [
153 | (master_doc, 'opensea',
154 | 'OpenSea API Python wrapper documentation',
155 | author,
156 | 'opensea',
157 | 'Python 3 wrapper for the OpenSea API',
158 | 'Miscellaneous'),
159 | ]
160 |
161 |
162 |
163 |
--------------------------------------------------------------------------------
/docs/contributing.md:
--------------------------------------------------------------------------------
1 | ```{include} ../CONTRIBUTING.md
2 | ```
3 |
--------------------------------------------------------------------------------
/docs/history.md:
--------------------------------------------------------------------------------
1 | ```{include} ../HISTORY.md
2 | ```
3 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # OpenSea API Python wrapper
2 |
3 | ```{toctree}
4 | :maxdepth: 2
5 | readme
6 | usage-examples
7 | contributing
8 | history
9 | ```
10 |
--------------------------------------------------------------------------------
/docs/readme.md:
--------------------------------------------------------------------------------
1 | ```{include} ../README.md
2 | ```
3 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | myst-parser==0.15.2
2 | sphinx-rtd-theme==1.0.0
--------------------------------------------------------------------------------
/docs/usage-examples.md:
--------------------------------------------------------------------------------
1 | # Usage examples
2 |
3 | ## Get data about a single asset
4 | ```python
5 | from opensea import OpenseaAPI
6 |
7 | api = OpenseaAPI(apikey="")
8 | result = api.asset(asset_contract_address="0x495f947276749Ce646f68AC8c248420045cb7b5e",
9 | token_id="66406747123743156841746366950152533278033835913591691491127082341586364792833")
10 | print(result)
11 | ```
12 |
13 | ## Get data about a single asset contract
14 | ```python
15 | from opensea import OpenseaAPI
16 |
17 | api = OpenseaAPI(apikey="")
18 | result = api.contract(asset_contract_address="0x495f947276749Ce646f68AC8c248420045cb7b5e")
19 | print(result)
20 | ```
21 |
22 | ## Get data about a single collection
23 | ```python
24 | from opensea import OpenseaAPI
25 |
26 | api = OpenseaAPI(apikey="")
27 | result = api.collection(collection_slug="cryptopunks")
28 | print(result)
29 | ```
30 |
31 | ## Get collection stats
32 | ```python
33 | from opensea import OpenseaAPI
34 |
35 | api = OpenseaAPI(apikey="")
36 | result = api.collection_stats(collection_slug="cryptopunks")
37 | print(result)
38 | ```
39 |
40 | ## Get data about multiple assets
41 | This example fetches three NFTs that Snoop Dogg owns:
42 | ```python
43 | from opensea import OpenseaAPI
44 |
45 | api = OpenseaAPI(apikey="")
46 | result = api.assets(owner="0xce90a7949bb78892f159f428d0dc23a8e3584d75",
47 | limit=3)
48 | print(result)
49 | ```
50 |
51 | ## Get data about multiple collections
52 | This example creates a JSON file with 3 collections where Snoop Dogg is an owner:
53 | ```python
54 | from opensea import OpenseaAPI
55 |
56 | api = OpenseaAPI(apikey="")
57 | result = api.collections(asset_owner="0xce90a7949bb78892f159f428d0dc23a8e3584d75",
58 | limit=3,
59 | export_file_name='snoop_collections.json')
60 | print(result)
61 | ```
62 |
63 | ## Get data about multiple events
64 | This example creates a JSON file with 10 events that happened between the
65 | defined time period (UTC timezone) between `2021-11-06 14:25` and `2021-11-06 14:30`
66 | ```python
67 | from opensea import OpenseaAPI
68 | from opensea import utils
69 |
70 | api = OpenseaAPI(apikey="")
71 | period_start = utils.datetime_utc(2021, 11, 6, 14, 25)
72 | period_end = utils.datetime_utc(2021, 11, 6, 14, 30)
73 | result = api.events(occurred_after=period_start,
74 | occurred_before=period_end,
75 | limit=10,
76 | export_file_name='events.json')
77 | print(result)
78 | ```
79 |
80 | ## Get data about multiple bundles
81 | ```python
82 | from opensea import OpenseaAPI
83 |
84 | api = OpenseaAPI(apikey="")
85 | result = api.bundles(limit=3)
86 | print(result)
87 | ```
--------------------------------------------------------------------------------
/opensea/__init__.py:
--------------------------------------------------------------------------------
1 | """Top-level package for OpenSea API Python wrapper."""
2 |
3 | __author__ = """Attila Toth"""
4 | __email__ = "hello@attilatoth.dev"
5 | __version__ = '0.1.7'
6 | __all__ = ["Events", "Asset", "Assets", "Contract", "Collection",
7 | "CollectionStats", "Collections", "Bundles", "utils",
8 | "OpenseaAPI"]
9 |
10 | from opensea.opensea import Events, Asset, Assets, Contract, Collection, \
11 | CollectionStats, Collections, Bundles
12 | from opensea.opensea_api import OpenseaAPI
13 | from opensea import utils
14 |
--------------------------------------------------------------------------------
/opensea/opensea.py:
--------------------------------------------------------------------------------
1 | import requests
2 | from datetime import datetime
3 | from opensea import utils
4 |
5 |
6 | class OpenseaBase:
7 |
8 | def __init__(self, endpoint, version="v1",
9 | base_url="https://api.opensea.io/api"):
10 | """Base class to interact with the OpenSea API and fetch NFT data.
11 |
12 | Args:
13 | endpoint (str): OpenSea API endpoint, eg. 'asset' or 'collections'
14 | version (str, optional): API version. Defaults to "v1"
15 | base
16 | """
17 | self.api_url = f"{base_url}/{version}/{endpoint}"
18 |
19 | def _make_request(self, params=None, export_file_name="",
20 | return_response=False):
21 | """Makes a request to the OpenSea API and returns either a response
22 | object or dictionary.
23 |
24 | Args:
25 | params (dict, optional): Query parameters to include in the
26 | request. Defaults to None.
27 | export_file_name (str, optional): In case you want to download the
28 | data into a file,
29 | specify the filename here. Eg. 'export.json'. Be default, no file
30 | is created.
31 | return_response (bool, optional): Set it True if you want it to
32 | return the actual response object.
33 | By default, it's False, which means a dictionary will be returned.
34 |
35 | Raises:
36 | ValueError: returns the error message from the API in case one
37 | (or more) of your request parameters are incorrect.
38 |
39 | Returns:
40 | Data sent back from the API. Either a response or dict object
41 | depending on the *return_response* argument.
42 | """
43 | response = requests.get(self.api_url, params=params)
44 | if response.status_code == 400:
45 | raise ValueError(response.text)
46 | elif response.status_code == 504:
47 | raise TimeoutError("The server reported a gateway time-out error.")
48 | if export_file_name != "":
49 | utils.export_file(response.content, export_file_name)
50 | if return_response:
51 | return response
52 | return response.json()
53 |
54 |
55 | class Events(OpenseaBase):
56 |
57 | MAX_API_ITEMS = 300
58 |
59 | def __init__(self):
60 | """Endpoint to fetch data about multiple events.
61 | More info about this endpoint in the OpenSea docs:
62 | https://docs.opensea.io/reference/retrieving-asset-events
63 | """
64 | super().__init__(endpoint="events")
65 |
66 | def fetch(
67 | self,
68 | asset_contract_address=None,
69 | collection_slug=None,
70 | token_id=None,
71 | account_address=None,
72 | event_type=None,
73 | only_opensea=False,
74 | auction_type=None,
75 | offset=0,
76 | limit=None,
77 | occurred_before=None,
78 | occurred_after=None,
79 | export_file_name="",
80 | ):
81 | """Fetches Events data from the API. Function arguments will be passed
82 | as API query parameters.
83 |
84 | OpenSea API Events query parameters:
85 | https://docs.opensea.io/reference/retrieving-asset-events
86 |
87 | All arguments will be passed without modification, except
88 | *occurred_before* and *occurred_after*. For these two args, you need
89 | to use datetime objects when calling this function.
90 |
91 | There's one extra optional argument:
92 | export_file_name (str, optional): Exports the JSON data into a the
93 | specified file.
94 |
95 | Returns:
96 | [dict]: Events data
97 | """
98 | if (occurred_after is not None
99 | and not isinstance(occurred_after, datetime)):
100 | raise ValueError("occurred_after must be a datetime object")
101 |
102 | if (occurred_before is not None
103 | and not isinstance(occurred_before, datetime)):
104 | raise ValueError("occurred_before must be a datetime object")
105 |
106 | query_params = {
107 | "asset_contract_address": asset_contract_address,
108 | "collection_slug": collection_slug,
109 | "token_id": token_id,
110 | "account_address": account_address,
111 | "event_type": event_type,
112 | "only_opensea": only_opensea,
113 | "auction_type": auction_type,
114 | "offset": offset,
115 | "limit": self.MAX_API_ITEMS if limit is None else limit,
116 | }
117 | if occurred_before is not None:
118 | query_params["occurred_before"] = occurred_before.timestamp()
119 | if occurred_after is not None:
120 | query_params["occurred_after"] = occurred_after.timestamp()
121 | return super()._make_request(query_params, export_file_name)
122 |
123 |
124 | class Asset(OpenseaBase):
125 | def __init__(self, asset_contract_address, token_id):
126 | """Endpoint to fetch data about a single asset.
127 | More info about this endpoint in the OpenSea docs:
128 | https://docs.opensea.io/reference/retrieving-a-single-asset
129 |
130 | Args:
131 | asset_contract_address (str)
132 | token_id (str):
133 | """
134 | super().__init__(endpoint=f"asset/{asset_contract_address}/{token_id}")
135 |
136 | def fetch(self, account_address=None, export_file_name=""):
137 | """Fetches Asset data from the API.
138 |
139 | Args:
140 | account_address (str, optional). Defaults to None.
141 | export_file_name (str, optional): Exports the JSON data into a the
142 | specified file.
143 |
144 | Returns:
145 | [dict]: Single asset data
146 | """
147 | query_params = {"account_address": account_address}
148 | return super()._make_request(query_params, export_file_name)
149 |
150 |
151 | class Assets(OpenseaBase):
152 |
153 | MAX_API_ITEMS = 50
154 |
155 | def __init__(self):
156 | """Endpoint to fetch data about multiple assets.
157 | More info about this endpoint in the OpenSea docs:
158 | https://docs.opensea.io/reference/getting-assets
159 | """
160 | super().__init__(endpoint="assets")
161 |
162 | def fetch(
163 | self,
164 | owner=None,
165 | token_ids=[],
166 | asset_contract_address=None,
167 | asset_contract_addresses=None,
168 | order_by=None,
169 | order_direction=None,
170 | offset=None,
171 | limit=None,
172 | collection=None,
173 | export_file_name="",
174 | ):
175 | """Fetches assets data from the API. Function arguments will be passed
176 | as API query parameters, without modification.
177 |
178 | OpenSea API Assets query parameters:
179 | https://docs.opensea.io/reference/getting-assets
180 |
181 | There's one extra optional argument:
182 | export_file_name (str, optional): Exports the JSON data into a the
183 | specified file.
184 |
185 | Returns:
186 | [dict]: Assets data
187 | """
188 | query_params = {
189 | "owner": owner,
190 | "token_ids": token_ids,
191 | "asset_contract_address": asset_contract_address,
192 | "asset_contract_addresses": asset_contract_addresses,
193 | "order_by": order_by,
194 | "order_direction": order_direction,
195 | "offset": offset,
196 | "limit": self.MAX_API_ITEMS if limit is None else limit,
197 | "collection": collection,
198 | }
199 | return super()._make_request(query_params, export_file_name)
200 |
201 |
202 | class Contract(OpenseaBase):
203 | def __init__(self, asset_contract_address):
204 | """Endpoint to fetch data about a single asset contract.
205 | More info about this endpoint in the OpenSea docs:
206 | https://docs.opensea.io/reference/retrieving-a-single-contract
207 |
208 | Args:
209 | asset_contract_address (str)
210 | """
211 | super().__init__(endpoint=f"asset_contract/{asset_contract_address}")
212 |
213 | def fetch(self, export_file_name=""):
214 | """Fetches asset contract data from the API.
215 |
216 | OpenSea API Asset Contract query parameters:
217 | https://docs.opensea.io/reference/retrieving-a-single-contract
218 |
219 | Args:
220 | export_file_name (str, optional): Exports the JSON data into a the
221 | specified file.
222 |
223 | Returns:
224 | [dict]: Single asset contract data
225 | """
226 | return super()._make_request(None, export_file_name)
227 |
228 |
229 | class Collection(OpenseaBase):
230 | def __init__(self, collection_slug):
231 | """Endpoint to fetch data about a single asset contract.
232 | More info about this endpoint in the OpenSea docs:
233 | https://docs.opensea.io/reference/retrieving-a-single-collection
234 |
235 | Args:
236 | collection_slug (str)
237 | """
238 | super().__init__(endpoint=f"collection/{collection_slug}")
239 |
240 | def fetch(self, export_file_name=""):
241 | """Fetches collection data from the API.
242 |
243 | OpenSea API Collection query parameters:
244 | https://docs.opensea.io/reference/retrieving-a-single-collection
245 |
246 | Args:
247 | export_file_name (str, optional): Exports the JSON data into a the
248 | specified file.
249 |
250 | Returns:
251 | [dict]: Single collection data
252 | """
253 | return super()._make_request(None, export_file_name)
254 |
255 |
256 | class CollectionStats(OpenseaBase):
257 | def __init__(self, collection_slug):
258 | """Endpoint to fetch a single collection's stats.
259 | More info about this endpoint in the OpenSea docs:
260 | https://docs.opensea.io/reference/retrieving-collection-stats
261 |
262 | Args:
263 | collection_slug (str)
264 | """
265 | super().__init__(endpoint=f"collection/{collection_slug}/stats")
266 |
267 | def fetch(self, export_file_name=""):
268 | """Fetches collection stats data from the API.
269 |
270 | OpenSea API Collection Stats query parameters:
271 | https://docs.opensea.io/reference/retrieving-collection-stats
272 |
273 | Args:
274 | export_file_name (str, optional): Exports the JSON data into the
275 | specified file.
276 |
277 | Returns:
278 | [dict]: Collection stats
279 | """
280 | return super()._make_request(None, export_file_name)
281 |
282 |
283 | class Collections(OpenseaBase):
284 |
285 | MAX_API_ITEMS = 300
286 |
287 | def __init__(self):
288 | """Endpoint to fetch data about multiple collections.
289 | More info about this endpoint in the OpenSea docs:
290 | https://docs.opensea.io/reference/retrieving-collections
291 | """
292 | super().__init__(endpoint="collections")
293 |
294 | def fetch(self, asset_owner=None, offset=None, limit=None,
295 | export_file_name=""):
296 | """Fetches Collections data from the API. Function arguments will be
297 | passed as API query parameters,
298 | without modification.
299 |
300 | OpenSea API Collections query parameters:
301 | https://docs.opensea.io/reference/retrieving-collections
302 |
303 | There's one extra optional argument:
304 | export_file_name (str, optional): Exports the JSON data into a the
305 | specified file.
306 |
307 | Returns:
308 | [dict]: Collections data
309 | """
310 | query_params = {
311 | "asset_owner": asset_owner,
312 | "offset": offset,
313 | "limit": self.MAX_API_ITEMS if limit is None else limit,
314 | }
315 | return super()._make_request(query_params, export_file_name)
316 |
317 |
318 | class Bundles(OpenseaBase):
319 |
320 | MAX_API_ITEMS = 50
321 |
322 | def __init__(self):
323 | super().__init__(endpoint="bundles")
324 |
325 | def fetch(
326 | self,
327 | on_sale=None,
328 | owner=None,
329 | asset_contract_address=None,
330 | asset_contract_addresses=[],
331 | token_ids=[],
332 | limit=None,
333 | export_file_name="",
334 | offset=None,
335 | ):
336 | """Fetches Bundles data from the API. Function arguments will be
337 | passed as API query parameters,
338 | without modification.
339 |
340 | OpenSea API Bundles query parameters:
341 | https://docs.opensea.io/reference/retrieving-bundles
342 |
343 | There's one extra optional argument:
344 | export_file_name (str, optional): Exports the JSON data into a the
345 | specified file.
346 |
347 | Returns:
348 | [dict]: Bundles data
349 | """
350 | query_params = {
351 | "on_sale": on_sale,
352 | "owner": owner,
353 | "asset_contract_address": asset_contract_address,
354 | "asset_contract_addresses": asset_contract_addresses,
355 | "token_ids": token_ids,
356 | "limit": self.MAX_API_ITEMS if limit is None else limit,
357 | "offset": offset,
358 | }
359 | return super()._make_request(query_params, export_file_name)
360 |
--------------------------------------------------------------------------------
/opensea/opensea_api.py:
--------------------------------------------------------------------------------
1 | import time
2 | import requests
3 | from datetime import datetime
4 | from opensea import utils
5 |
6 |
7 | class OpenseaAPI:
8 |
9 | MAX_EVENT_ITEMS = 300
10 | MAX_ASSET_ITEMS = 50
11 | MAX_COLLECTION_ITEMS = 300
12 | MAX_BUNDLE_ITEMS = 50
13 | MAX_LISTING_ITEMS = 50
14 | MAX_OFFER_ITEMS = 50
15 |
16 | def __init__(self, base_url="https://api.opensea.io/api", apikey=None,
17 | version="v1"):
18 | """Base class to interact with the OpenSea API and fetch NFT data.
19 |
20 | Args:
21 | base_url (str): OpenSea API base URL. Defaults to
22 | "https://api.opensea.io/api".
23 | apikey (str): OpenSea API key (you need to request one)
24 | version (str, optional): API version. Defaults to "v1".
25 | """
26 | self.api_url = f"{base_url}/{version}"
27 | self.apikey = apikey
28 |
29 | def _make_request(self, endpoint=None, params=None, export_file_name="",
30 | return_response=False):
31 | """Makes a request to the OpenSea API and returns either a response
32 | object or dictionary.
33 |
34 | Args:
35 | endpoint (str, optional): API endpoint to use for the request.
36 | params (dict, optional): Query parameters to include in the
37 | request. Defaults to None.
38 | export_file_name (str, optional): In case you want to download the
39 | data into a file,
40 | specify the filename here. Eg. 'export.json'. Be default, no file
41 | is created.
42 | return_response (bool, optional): Set it True if you want it to
43 | return the actual response object.
44 | By default, it's False, which means a dictionary will be returned.
45 | next_url (str, optional): If you want to paginate, provide the
46 | `next` value here (this is a URL) OpenSea provides in the response.
47 | If this argument is provided, `endpoint` will be ignored.
48 |
49 | Raises:
50 | ValueError: returns the error message from the API in case one
51 | (or more) of your request parameters are incorrect.
52 | HTTPError: Unauthorized access. Try using an API key.
53 | ConnectionError: your request got blocked by the server (try
54 | using an API key if you keep getting this error)
55 | TimeoutError: your request timed out (try rate limiting)
56 |
57 | Returns:
58 | Data sent back from the API. Either a response or dict object
59 | depending on the `return_response` argument.
60 | """
61 | if endpoint is None:
62 | raise ValueError(
63 | """You need to define an `endpoint` when
64 | making a request!"""
65 | )
66 |
67 | headers = {"X-API-KEY": self.apikey}
68 | url = f"{self.api_url}/{endpoint}"
69 | response = requests.get(url, headers=headers, params=params)
70 | if response.status_code == 400:
71 | raise ValueError(response.text)
72 | elif response.status_code == 401:
73 | raise requests.exceptions.HTTPError(response.text)
74 | elif response.status_code == 403:
75 | raise ConnectionError("The server blocked access.")
76 | elif response.status_code == 495:
77 | raise requests.exceptions.SSLError("SSL certificate error")
78 | elif response.status_code == 504:
79 | raise TimeoutError("The server reported a gateway time-out error.")
80 |
81 | if export_file_name != "":
82 | utils.export_file(response.content, export_file_name)
83 | if return_response:
84 | return response
85 | return response.json()
86 |
87 | def events(
88 | self,
89 | asset_contract_address=None,
90 | collection_slug=None,
91 | token_id=None,
92 | account_address=None,
93 | event_type=None,
94 | only_opensea=False,
95 | auction_type=None,
96 | limit=None,
97 | occurred_before=None,
98 | occurred_after=None,
99 | collection_editor=None,
100 | export_file_name="",
101 | ):
102 | """Fetches Events data from the API. Function arguments will be passed
103 | as API query parameters.
104 |
105 | OpenSea API Events query parameters:
106 | https://docs.opensea.io/reference/retrieving-asset-events
107 |
108 | All arguments will be passed without modification, except
109 | *occurred_before* and *occurred_after*. For these two args, you need
110 | to use datetime objects when calling this function.
111 |
112 | There's one extra optional argument:
113 | export_file_name (str, optional): Exports the JSON data into the
114 | specified file.
115 |
116 | Returns:
117 | [dict]: Events data
118 | """
119 | if occurred_before is not None and not isinstance(occurred_before,
120 | datetime):
121 | raise ValueError("`occurred_before` must be a datetime object")
122 |
123 | if occurred_after is not None and not isinstance(occurred_after,
124 | datetime):
125 | raise ValueError("`occurred_after` must be a datetime object")
126 |
127 | query_params = {
128 | "asset_contract_address": asset_contract_address,
129 | "collection_slug": collection_slug,
130 | "token_id": token_id,
131 | "account_address": account_address,
132 | "event_type": event_type,
133 | "only_opensea": only_opensea,
134 | "auction_type": auction_type,
135 | "collection_editor": collection_editor,
136 | "limit": self.MAX_EVENT_ITEMS if limit is None else limit,
137 | }
138 | if occurred_before is not None:
139 | query_params["occurred_before"] = occurred_before.timestamp()
140 |
141 | if occurred_after is not None:
142 | query_params["occurred_after"] = occurred_after.timestamp()
143 | return self._make_request("events", query_params, export_file_name)
144 |
145 | def events_backfill(
146 | self,
147 | start,
148 | until,
149 | rate_limiting=2,
150 | asset_contract_address=None,
151 | collection_slug=None,
152 | token_id=None,
153 | account_address=None,
154 | event_type=None,
155 | only_opensea=False,
156 | auction_type=None,
157 | limit=None,
158 | collection_editor=None,
159 | ):
160 | """
161 | EXPERIMENTAL FUNCTION!
162 |
163 | Expected behaviour:
164 |
165 | Download events and paginate over multiple pages until the given
166 | time is reached. Pagination happens **backwards** (so you can use this
167 | function to **backfill** events data eg. into a database) from `start`
168 | until `until`.
169 |
170 | The function returns a generator.
171 |
172 | Args:
173 | start (datetime): A point in time where you want to start
174 | downloading data from.
175 | until (datetime): How much data do you want?
176 | How much do you want to go back in time? This datetime value will
177 | provide that threshold.
178 | rate_limiting (int, optional): Seconds to wait between requests.
179 | Defaults to 2.
180 |
181 | Other parameters are available (all of the `events` endpoint
182 | parameters) and they are documented in the OpenSea docs
183 | https://docs.opensea.io/reference/retrieving-asset-events
184 |
185 | Yields:
186 | dictionary: event data
187 | """
188 | if not isinstance(until, datetime) or not isinstance(start, datetime):
189 | raise ValueError("`until` and `start` must be datetime objects")
190 |
191 | if until > start:
192 | raise ValueError(
193 | """`start` must be a later point in time
194 | than `until`"""
195 | )
196 |
197 | query_params = {
198 | "asset_contract_address": asset_contract_address,
199 | "collection_slug": collection_slug,
200 | "token_id": token_id,
201 | "account_address": account_address,
202 | "event_type": event_type,
203 | "only_opensea": only_opensea,
204 | "auction_type": auction_type,
205 | "limit": self.MAX_EVENT_ITEMS if limit is None else limit,
206 | "occurred_before": start,
207 | "collection_editor": collection_editor,
208 | }
209 |
210 | # make the first request to get the `next` cursor value
211 | first_request = self._make_request("events", query_params)
212 | yield first_request
213 | query_params["cursor"] = first_request["next"]
214 |
215 | # paginate
216 | while True:
217 | time.sleep(rate_limiting)
218 | data = self._make_request("events", query_params)
219 |
220 | # update the `next` parameter for the upcoming request
221 | query_params["cursor"] = data["next"]
222 |
223 | time_field = data["asset_events"][0]["created_date"]
224 | current_time = utils.str_to_datetime_utc(time_field)
225 | if current_time >= until:
226 | yield data
227 | else:
228 | break
229 | yield None
230 |
231 | def asset(
232 | self,
233 | asset_contract_address,
234 | token_id,
235 | account_address=None,
236 | include_orders=False,
237 | export_file_name="",
238 | ):
239 | """Fetches Asset data from the API.
240 |
241 | Args:
242 | OpenSea API Asset query parameters:
243 | https://docs.opensea.io/reference/retrieving-a-single-asset
244 |
245 | Extra args:
246 | export_file_name (str, optional): Exports the JSON data into a the
247 | specified file.
248 |
249 | Returns:
250 | [dict]: Single asset data
251 | """
252 | endpoint = f"asset/{asset_contract_address}/{token_id}"
253 | query_params = {"account_address": account_address,
254 | "include_orders": include_orders}
255 | return self._make_request(endpoint, query_params, export_file_name)
256 |
257 | def assets(
258 | self,
259 | owner=None,
260 | token_ids=[],
261 | asset_contract_address=None,
262 | asset_contract_addresses=None,
263 | order_by=None,
264 | order_direction=None,
265 | offset=None,
266 | limit=None,
267 | collection=None,
268 | include_orders=False,
269 | pagination=False,
270 | rate_limiting=2,
271 | export_file_name="",
272 | ):
273 | """Fetches assets data from the API. Function arguments will be passed
274 | as API query parameters, without modification.
275 |
276 | OpenSea API Assets query parameters:
277 | https://docs.opensea.io/reference/getting-assets
278 |
279 | There are extra optional arguments:
280 | pagination (boolean, optional): Whether you want to get only the first
281 | page of assets, or all of them. If it's `True` it will use the
282 | cursor-based pagination provided by OpenSea. Defaults to False.
283 | rate_limiting (int, optional): Only relevant if pagination is `True`.
284 | It applies a rate limitation in-between requests. Defaults to 2 sec.
285 | export_file_name (str, optional): Exports the JSON data into the
286 | specified file. If pagination is `True` this argument is ignored.
287 |
288 | Returns:
289 | [dict]: Assets data
290 | """
291 | query_params = {
292 | "owner": owner,
293 | "token_ids": token_ids,
294 | "asset_contract_address": asset_contract_address,
295 | "asset_contract_addresses": asset_contract_addresses,
296 | "order_by": order_by,
297 | "order_direction": order_direction,
298 | "offset": offset,
299 | "limit": self.MAX_ASSET_ITEMS if limit is None else limit,
300 | "collection": collection,
301 | "include_orders": include_orders
302 | }
303 | if pagination:
304 | # make the first request to get the `next` cursor value
305 | first_request = self._make_request("assets", query_params)
306 | yield first_request
307 | query_params["cursor"] = first_request.get("next")
308 |
309 | # paginate
310 | while True:
311 | time.sleep(rate_limiting)
312 | if query_params["cursor"] is not None:
313 | response = self._make_request("assets", query_params)
314 | yield response
315 | query_params["cursor"] = response.get("next")
316 | else:
317 | break # stop pagination if there is no next page
318 | else:
319 | return self._make_request("assets", query_params, export_file_name)
320 |
321 | def contract(self, asset_contract_address, export_file_name=""):
322 | """Fetches asset contract data from the API.
323 |
324 | OpenSea API Asset Contract query parameters:
325 | https://docs.opensea.io/reference/retrieving-a-single-contract
326 |
327 | Args:
328 | asset_contract_address (str): Contract address of the NFT.
329 | export_file_name (str, optional): Exports the JSON data into a the
330 | specified file.
331 |
332 | Returns:
333 | [dict]: Single asset contract data
334 | """
335 | endpoint = f"asset_contract/{asset_contract_address}"
336 | return self._make_request(endpoint, export_file_name=export_file_name)
337 |
338 | def collection(self, collection_slug, export_file_name=""):
339 | """Fetches collection data from the API.
340 |
341 | OpenSea API Collection query parameters:
342 | https://docs.opensea.io/reference/retrieving-a-single-collection
343 |
344 | Args:
345 | collection_slug (str): Collection slug (unique identifer)
346 | export_file_name (str, optional): Exports the JSON data into a the
347 | specified file.
348 |
349 | Returns:
350 | [dict]: Single collection data
351 | """
352 | endpoint = f"collection/{collection_slug}"
353 | return self._make_request(endpoint, export_file_name=export_file_name)
354 |
355 | def collection_stats(self, collection_slug, export_file_name=""):
356 | """Fetches collection stats data from the API.
357 |
358 | OpenSea API Collection Stats query parameters:
359 | https://docs.opensea.io/reference/retrieving-collection-stats
360 |
361 | Args:
362 | export_file_name (str, optional): Exports the JSON data into the
363 | specified file.
364 |
365 | Returns:
366 | [dict]: Collection stats
367 | """
368 | endpoint = f"collection/{collection_slug}/stats"
369 | return self._make_request(endpoint, export_file_name=export_file_name)
370 |
371 | def collections(
372 | self, asset_owner=None, offset=None, limit=None, export_file_name=""
373 | ):
374 | """Fetches Collections data from the API. Function arguments will be
375 | passed as API query parameters,
376 | without modification.
377 |
378 | OpenSea API Collections query parameters:
379 | https://docs.opensea.io/reference/retrieving-collections
380 |
381 | There's one extra optional argument:
382 | export_file_name (str, optional): Exports the JSON data into a the
383 | specified file.
384 |
385 | Returns:
386 | [dict]: Collections data
387 | """
388 | query_params = {
389 | "asset_owner": asset_owner,
390 | "offset": offset,
391 | "limit": self.MAX_COLLECTION_ITEMS if limit is None else limit,
392 | }
393 | return self._make_request("collections", query_params,
394 | export_file_name)
395 |
396 | def bundles(
397 | self,
398 | on_sale=None,
399 | owner=None,
400 | asset_contract_address=None,
401 | asset_contract_addresses=[],
402 | token_ids=[],
403 | limit=None,
404 | export_file_name="",
405 | offset=None,
406 | ):
407 | """Fetches Bundles data from the API. Function arguments will be
408 | passed as API query parameters,
409 | without modification.
410 |
411 | OpenSea API Bundles query parameters:
412 | https://docs.opensea.io/reference/retrieving-bundles
413 |
414 | There's one extra optional argument:
415 | export_file_name (str, optional): Exports the JSON data into a the
416 | specified file.
417 |
418 | Returns:
419 | [dict]: Bundles data
420 | """
421 | query_params = {
422 | "on_sale": on_sale,
423 | "owner": owner,
424 | "asset_contract_address": asset_contract_address,
425 | "asset_contract_addresses": asset_contract_addresses,
426 | "token_ids": token_ids,
427 | "limit": self.MAX_BUNDLE_ITEMS if limit is None else limit,
428 | "offset": offset,
429 | }
430 | return self._make_request("bundles", query_params, export_file_name)
431 |
432 | def listings(
433 | self, asset_contract_address, token_id, limit=None, export_file_name=""
434 | ):
435 | """Fetches Listings data for an asset from the API. Function arguments
436 | will be passed as API query parameters, without modification.
437 |
438 | OpenSea API Listings query parameters:
439 | https://docs.opensea.io/reference/asset-listings
440 |
441 | There's one extra optional argument:
442 | export_file_name (str, optional): Exports the JSON data into a the
443 | specified file.
444 |
445 | Returns:
446 | [dict]: Listings data
447 | """
448 | query_params = {
449 | "limit": self.MAX_LISTING_ITEMS if limit is None else limit
450 | }
451 | endpoint = f"asset/{asset_contract_address}/{token_id}/listings"
452 | return self._make_request(endpoint, query_params, export_file_name)
453 |
454 | def offers(self, asset_contract_address, token_id, limit=None,
455 | export_file_name=""):
456 | """Fetches Offers data for an asset from the API. Function arguments
457 | will be passed as API query parameters, without modification.
458 |
459 | OpenSea API Listings query parameters:
460 | https://docs.opensea.io/reference/asset-offers
461 |
462 | There's one extra optional argument:
463 | export_file_name (str, optional): Exports the JSON data into a the
464 | specified file.
465 |
466 | Returns:
467 | [dict]: Offers data
468 | """
469 | query_params = {
470 | "limit": self.MAX_OFFER_ITEMS if limit is None else limit
471 | }
472 | endpoint = f"asset/{asset_contract_address}/{token_id}/offers"
473 | return self._make_request(endpoint, query_params, export_file_name)
474 |
--------------------------------------------------------------------------------
/opensea/utils.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime, timezone
2 |
3 |
4 | def export_file(content, file_name):
5 | """Creates a new file with the specified content and file name. If the file
6 | already exists, overwrites it.
7 |
8 | Args:
9 | content (str or bytes): Content to be inserted into the file.
10 | file_name (str): Name of the file to be created. Eg. 'export.json'.
11 | """
12 | with open(file_name, "wb") as f:
13 | f.write(content)
14 |
15 |
16 | def str_to_datetime_utc(str):
17 | """Converts a string into UTC datetime object.
18 |
19 | Args:
20 | str (str): String timestamp.
21 |
22 | Returns:
23 | datetime: Datetime object.
24 | """
25 | return datetime.fromisoformat(str).replace(tzinfo=timezone.utc)
26 |
27 |
28 | def datetime_utc(year, month, day, hour, minute):
29 | """Returns a datetime object in UTC timezone
30 |
31 | Args:
32 | year (int): eg. 2021
33 | month (int): between 1-12
34 | day (int): between 1-7
35 | hour (int): between 0-23
36 | minute (int): between 0-59
37 |
38 | Returns:
39 | datetime
40 | """
41 | return datetime(year, month, day, hour, minute, tzinfo=timezone.utc)
42 |
--------------------------------------------------------------------------------
/requirements_dev.txt:
--------------------------------------------------------------------------------
1 | pip==21.1
2 | bump2version==0.5.11
3 | wheel==0.38.1
4 | watchdog==0.9.0
5 | flake8==3.7.8
6 | tox==3.14.0
7 | coverage==4.5.4
8 | Sphinx==3.1
9 | myst-parser==0.15.2
10 | sphinx-rtd-theme==1.0.0
11 | twine==1.14.0
12 | pytest==6.2.4
13 | black==21.7b0
14 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [bumpversion]
2 | current_version = 0.1.7
3 | commit = True
4 | tag = True
5 |
6 | [bumpversion:file:setup.py]
7 | search = version='{current_version}'
8 | replace = version='{new_version}'
9 |
10 | [bumpversion:file:opensea/__init__.py]
11 | search = __version__ = '{current_version}'
12 | replace = __version__ = '{new_version}'
13 |
14 | [bdist_wheel]
15 | universal = 1
16 |
17 | [flake8]
18 | exclude = docs
19 |
20 | [tool:pytest]
21 | collect_ignore = ['setup.py']
22 |
23 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | """The setup script."""
4 |
5 | from setuptools import setup, find_packages
6 |
7 | with open('README.md') as readme_file:
8 | readme = readme_file.read()
9 |
10 | with open('HISTORY.md') as history_file:
11 | history = history_file.read()
12 |
13 | requirements = ['requests>=2.26.0']
14 |
15 | test_requirements = ['pytest>=3', ]
16 |
17 | setup(
18 | author="Attila Toth",
19 | author_email='hello@attilatoth.dev',
20 | python_requires='>=3.6',
21 | classifiers=[
22 | 'Development Status :: 4 - Beta',
23 | 'Intended Audience :: Developers',
24 | 'Environment :: Web Environment',
25 | 'License :: OSI Approved :: MIT License',
26 | 'Natural Language :: English',
27 | 'Programming Language :: Python',
28 | 'Programming Language :: Python :: 3',
29 | 'Programming Language :: Python :: 3.6',
30 | 'Programming Language :: Python :: 3.7',
31 | 'Programming Language :: Python :: 3.8',
32 | 'Topic :: Internet',
33 | 'Topic :: Internet :: WWW/HTTP :: HTTP Servers',
34 | 'Topic :: Office/Business :: Financial',
35 | 'Topic :: Software Development :: Libraries',
36 | 'Topic :: Software Development :: Libraries :: Python Modules',
37 | 'Topic :: Utilities'
38 |
39 | ],
40 | description="Python 3 wrapper for the OpenSea NFT API",
41 | install_requires=requirements,
42 | license="MIT license",
43 | long_description=readme + "\n\n" + history,
44 | long_description_content_type="text/markdown",
45 | include_package_data=True,
46 | keywords=['opensea', 'nft', 'non fungible token', 'crypto'],
47 | maintainer='Attila Toth',
48 | maintainer_email='hello@attilatoth.dev',
49 | name='opensea-api',
50 | packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]),
51 | test_suite='tests',
52 | tests_require=test_requirements,
53 | url='https://github.com/zseta/python-opensea',
54 | project_urls={
55 | 'Documentation': 'https://opensea-api.attilatoth.dev',
56 | 'Source': 'https://github.com/zseta/python-opensea'
57 | },
58 | version='0.1.7',
59 | zip_safe=False,
60 | )
61 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
1 | """Unit test package for opensea."""
2 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py38, py39, flake8
3 |
4 | [testenv:flake8]
5 | basepython = python
6 | deps = flake8
7 | commands = flake8 opensea tests --ignore E124,E125,E129,W503,W504
8 |
9 | [testenv]
10 | setenv =
11 | PYTHONPATH = {toxinidir}
12 | deps = -r {toxinidir}/requirements_dev.txt
13 | ; If you want to make tox run the tests with the same versions, create a
14 | ; requirements.txt with the pinned versions and uncomment the following line:
15 | ; -r{toxinidir}/requirements.txt
16 | commands =
17 | pip install -U pip
18 | pytest --basetemp={envtmpdir}
19 | pytest {posargs}
20 |
--------------------------------------------------------------------------------