Package: genshinstats
genshinstatsGenshinstats is an unofficial wrapper for the Genshin Impact api. It supports getting user stats, wish history and automatic claiming of daily check-in rewards.If you want to use this library asynchronously or are simply looking for a better experience check outgenshin.pyhow to installusingpippip install genshinstatsAlternatives:using pip from gitpip install git+https://github.com/thesadru/genshinstatsclone and install manuallygit clone https://github.com/thesadru/genshinstats.git
cd genshinstats
python setup.py installhow to useYou simply need to import the module and use theset_cookiefunction to login.
Since all mihoyo's apis are private there's no kind of api token or authentication header, instead you are required to use your own account cookies. (how can I get my cookies?)The best way to learn is with examples so I have provided a usage example for every function.You may also use thedocumentationexamplesSimple examples of usage:importgenshinstatsasgsgs.set_cookie(ltuid=119480035,ltoken="cnF7TiZqHAAvYqgCBoSPx5EjwezOh1ZHoqSHf7dT")uid=710785423data=gs.get_user_stats(uid)total_characters=len(data['characters'])print('user "sadru" has a total of',total_characters,'characters')Cookies should be your own. These are just some example cookies of an account that can be deleted at any time.Note thatset_cookieandset_cookiesare different functions! The latter should only be used when getting data for other users (for example social media bots)stats=gs.get_user_stats(uid)['stats']forfield,valueinstats.items():print(f"{field}:{value}")# achievements: 210# active_days: 121# characters: 19# ...characters=gs.get_characters(uid)forcharincharacters:print(f"{char['rarity']}*{char['name']:10}| lvl{char['level']:2}C{char['constellation']}")# 5* Xiao       | lvl 90 C0# 4* Beidou     | lvl 80 C1# 4* Fischl     | lvl 80 C1# ...spiral_abyss=gs.get_spiral_abyss(uid,previous=True)stats=spiral_abyss['stats']forfield,valueinstats.items():print(f"{field}:{value}")# total_battles: 14# total_wins: 9# max_floor: 11-3# total_stars: 19notes=gs.get_notes(uid)print(f"Current resin:{notes['resin']}/{notes['max_resin']}")print(f"Current realm currency:{notes['realm_currency']}/{notes['max_realm_currency']}")print(f"Expeditions:{len(notes['expeditions'])}/{notes['max_expeditions']}")It's possible to set the cookies with a headergs.set_cookie("ltoken=cnF7TiZqHAAvYqgCBoSPx5EjwezOh1ZHoqSHf7dT; ltuid=119480035")Or set them automatically by getting them from a browsergs.set_cookie_auto()# search all browsersgs.set_cookie_auto('chrome')# search specific browserrequirescookie-browser3, can take up to 10ssubmoduleswishesGets your wish history.
For this you must first open the history/details page in genshin impact,
you can find the page in the wish menu on the bottom left.
The script is then able to get your authkey by itself and fetch the data with it.foriings.get_wish_history():print(f"{i['time']}-{i['name']}({i['rarity']}*{i['type']})")# 2021-03-22 09:50:12 - Razor (4* Character)# 2021-03-22 09:50:12 - Harbinger of Dawn (3* Weapon)# 2021-03-22 09:50:12 - Cool Steel (3* Weapon)# 2021-03-22 09:50:12 - Emerald Orb (3* Weapon)# ...types=gs.get_banner_types()# get all possible typesprint(types)# {100: 'Novice Wishes',#  200: 'Permanent Wish',#  301: 'Character Event Wish',#  302: 'Weapon Event Wish'}# get pulls only from a specific bannerforiings.get_wish_history(301):print(f"{i['time']}-{i['name']}({i['rarity']}*{i['type']})")banner_ids=gs.get_banner_ids()foriinbanner_ids:details=gs.get_banner_details(i)print(f"{details['banner_type_name']}-{details['banner']}")print('5 stars:',', '.join(i['name']foriindetails['r5_up_items']))print('4 stars:',', '.join(i['name']foriindetails['r4_up_items']))print()# Weapon Event Wish - Event Wish "Epitome Invocation"# 5 stars: Elegy for the End, Skyward Blade# 4 stars: The Alley Flash, Wine and Song, Favonius Greatsword, Favonius Warbow, Dragon's Baneget_banner_ids()gets a list of all banners whose details page has been opened in the game.Basically uses the same approach asget_authkeyView other's history by passing in an authkey:authkey="t5QMiyrenV50CFbqnB4Z+aG4ltprY1JxM5YoaChr9QH0Lp6rK5855xxa1P55..."gs.get_wish_history(size=20,authkey=authkey)Or by directly setting the authkey:# directly with the token:gs.set_authkey("D3ZYe49SUzpDgzrt/l00n2673Zg8N/Yd9OSc7NulRHhp8EhzlEnz2ISBtKBR0fZ/DGs8...")# get from a url:gs.set_authkey("https://webstatic-sea.mihoyo.com/ys/event/im-service/index.html?...")# read from a custom file:gs.set_authkey('other_output_log.txt')Since the authkey lasts only a day this is more like for exporting than for actual use.When importing from platforms like android you must use the authkey manually. (how can I get my authkey?)daily rewardsAutomatically get daily sign in rewards for the currently logged-in user.reward=gs.claim_daily_reward()ifrewardisnotNone:print(f"Claimed daily reward -{reward['cnt']}x{reward['name']}")else:print("Could not claim daily reward")You can also get a list of all rewards you have claimed so farforiings.get_claimed_rewards():print(i['cnt'],i['name'])# 20 Primogem# 5000 Mora# 3 Fine Enhancement Ore# 3 Adventurer's Experience# 8000 Moratransaction logsLogs for artifact, weapon, resin, genesis crystol and primogem "transactions".
You may view a history of everything you have gained in the last 3 months however you may not get the exact amount of any of these so it's generally meant for statistics of gain/loss.These functions require the same authkey as wish history.All of these functions work the same:get_primogem_log
get_resin_log
get_crystal_log
get_artifact_log
get_weapon_logforiings.get_primogem_log(size=40):print(f"{i['time']}-{i['reason']}:{i['amount']}primogems")# 2021-08-14 19:43:12 - Achievement reward: 5 primogems# 2021-08-13 18:32:58 - Daily Commission reward: 20 primogems# 2021-08-13 18:22:00 - Daily Commission reward: 10 primogems# 2021-08-13 18:19:05 - Daily Commission reward: 10 primogems# 2021-08-13 17:48:09 - Shop purchase: -1600 primogemstotal=0foriings.get_primogem_log():total+=i['amount']print(f"Since{i['time']}you have gained{total}primogems   ",end='\r')# Since 2021-01-02 20:35:16 you have gained 5197 primogemsSince you the api itself doesn't provide the current amount of resin genshinstats provides a way to calculate it yourself.
First it requires a reference to how much primogems you had at a specific time and then it calculates how much you should have currently.fromdatetimeimportdatetime# calculating the current amount of resin based on the fact you had 60 resin on the September 28th 2021 12:00 UTCprint(gs.current_resin(datetime(2021,9,28,12,00),60))Since mihoyo can take up to an hour to update their public database of logs this will only work as long as you haven't used any resin in the last hourIf you do not know how much resin you had at any point you can very roughly approximate it withgs.approximate_current_resin()hoyolabMiscalenious stuff for mihoyo's hoyolab. Has searching, auto check-in and code redemption.# search all users and get their nickname and uidforuserings.search('sadru'):print(f"{user['nickname']}({user['uid']}) -\"{user['introduce']}\"")# check in to hoyolabgs.hoyolab_check_in()# get a record card; has user nickname and game uidcard=gs.get_record_card(8366222)print(f"{card['nickname']}({card['game_role_id']}) - AR{card['level']}")# get an in-game uid from a hoyolab uid directlyuid=8366222# if it's an actual hoyolab uidifnotgs.is_game_uid(uid):uid=gs.get_uid_from_hoyolab_uid(uid)You can also redeem gift codes mihoyo gives out during events such as livestreamsgs.redeem_code('GENSHINGIFT')redeem_code()requires a special form of authentication: anaccount_idandcookie_tokencookies.
You can get these by grabbing cookies fromthe official genshin siteinstead of hoyolabs.cachingCaching is currently not native to genshinstats however there's a builtin way to install a cache into every function.This will cache everything install a cache into every* function in genshinstats.cache={}gs.install_cache(cache)The same cache reference is used accross all functions allowing you to see the entire cache and possibly even clear it.cache={}gs.install_cache(cache)gs.get_characters(710785423)print(cache.keys())# dict_keys([('get_user_stats', 710785423), ('get_characters', 710785423, None, 'en-us')])You're highly encouraged to use preexisting cache objects from libraries such ascachetoolsto allow for more functionalityfromcachetoolsimportCachecache=Cache(100)# only store maximum of 100 objects in the cachegs.install_cache(cache)However the majority of these approaches have a fatal flaw: if they are not periodically cleared then old data might get stuck in them. It's therefore recommended to keep a ttl cache or automatically clear the cache yourself.fromcachetoolsimportTTLCachecache=TTLCache(1024,3600)# max 1024 objects with max livespan of 1 hourgs.install_cache(cache)*Not every functioncanbe cached, some are dependent on cookies and some do not cleanly integrate with preexisting caches.Theinstall_cachefunction requires aMutableMapping[Tuple[Any, ...], Any]and only uses__geitem__,__setitem__and__contains__. You may and are encouraged to create your own cache objects.
In case you're considering storing your data in json files or similar it's encouraged to hash your keys before storing them as tuples as keys are generally not supported in other formats.change languageSome api endpoints support changing languages, you can see them listed here:get_charactersget_banner_typesget_wish_historyget_gacha_itemsget_banner_detailsclaim_daily_rewardYou can get a all language codes and their names withget_langs(){'de-de':'Deutsch','en-us':'English','es-es':'Español','fr-fr':'Français','id-id':'Indonesia','ja-jp':'日本語','ko-kr':'한국어','pt-pt':'Português','ru-ru':'Pусский','th-th':'ภาษาไทย','vi-vn':'Tiếng Việt','zh-cn':'简体中文','zh-tw':'繁體中文'}Any of these codes can then be passed as thelangparametercharacters=gs.get_characters(710785423,lang='zh-cn')print(characters)# {'name': '莫娜',#  'rarity': 5,#  'element': 'Hydro',#  ...#  'weapon': {'name': '万国诸海图谱',#             'rarity': 4,#             'type': '法器',#             'description': '详尽记载了大陆周边海流气候的海图，是从异国经由商路流落到璃月的奇异典籍。',#             ...},#  ...using genshinstats asynchronously (for example with a discord bot)You have probably noticed that while you're fetching data with genshinstats or any other sync library for that matter that other async functions never run.
To solve this issue you must turn the synchronous function into an asynchronous one.To use any function asynchronously you can use theasyncio.to_thread(func, *args, **kwargs)function.
If you're using python 3.8 or less you can useloop.run_in_executor(None, func, *args). Check out theasynciodocs for more info.importasyncioimportgenshinstatsasgsgs.set_cookie_auto()asyncdefmain():characters=awaitasyncio.to_thread(gs.get_characters,710785423)print(characters)asyncio.run(main())faqHow can I get my cookies?go tohoyolab.comlogin to your accountpressF12to open inspect mode (aka Developer Tools)go toApplication,Cookies,https://www.hoyolab.com.copyltuidandltokenuseset_cookie(ltuid=..., ltoken=...)in your codeIt is possible that ltuid or ltoken are for some reason not avalible in your cookies (blame it on mihoyo).
In this case there are probably the oldaccount_idandcookie_tokencookies, so use those withset_cookie(account_id=..., cookie_token=...).If not even those are avalible go tohttps://account.mihoyo.com/#/loginand use thelogin_ticketcookie inhttps://webapi-os.account.mihoyo.com/Api/cookie_accountinfo_by_loginticket?login_ticket=<...>automatic alternativeYou can callget_browser_cookiesto get a dictionary of cookies that are needed for authentication.importgenshinstatsasgscookies=gs.get_browser_cookies()print(cookies)# {'ltuid': '93827185', 'ltoken': 'aH0cEGX458eJjGoC2z0iiDHL7UGMz09ad0a9udwh'}You can then use these cookies in your actual code or save them as enviroment variablesgs.set_cookie(ltuid=93827185,ltoken='aH0cEGX458eJjGoC2z0iiDHL7UGMz09ad0a9udwh')You can also just set the cookies usingset_cookie_autowhich callsget_browser_cookiesevery time you run the scriptgs.set_cookie_auto()setting multiple cookies at onceMihoyo allows users to get data for only up to 30 other users per day, to circumvent this limitation you can set multiple cookies at once withset_cookies()gs.set_cookies({'ltuid':1,'ltoken':'token...'},{'ltuid':2,'ltoken':'other token...'})Creating alt accounts by hand is a lengthy and painful process so you can use one of the countless automated account creators likegenshin account creatorHow can I get my authkey?To get your authkey manually from other platforms you can use any of these approaches:PCOpen Paimon menu [ESC]Click FeedbackWait for it to load and a browser page should openCopy the linkAndroidOpen Paimon menuClick FeedbackWait for it to load and a feedback page should openTurn off your Wi-Fi and data connectionPress refresh on top right cornerThe page should display an error and show you some text with black fontHold the text and press select all, then copy that text (don't copy only some  portion of the text)IOSOpen Paimon menuClick FeedbackWait for it to load and a feedback page should openPress In-game issuePress Co-Op ModeThere is a link on the bottom of the reply; press thatA browser should open upCopy the linkPSOpen Genshin Impact on your PlayStationOpen the event mail that contains the QR CodeScan the QR Code with your phoneCopy the linkYou can only use this if you have an in-game mail with QR Code to open the web eventthis is largerly copied frompaimon.moeWhy do I keep gettingDataNotPublicerrors even though I'm trying to view my own account stats and didn't set anything to private?TheDataNotPublicis raised when a user has not made their data public, because the account visibility is set to private by default.
To solve this error You must go tohoyolab.comand make your account public by clickingthe toogle next to "public".How do the cookie and authkey work?Every endpoint in mihoyo's api requires authentication, this is in the form of a cookie and an authkey.
User stats use a cookie and wish history uses an authkey.The cookie is bound to the user and as far as I know can only be reset by changing your password, so remember to never give your cookie to anyone. For extra safety you may want to create an alt account, so your real account is never in any danger. This token will allow you to view public stats of all users and private stats of yourself.The authkey is a temporary token to access your wish history. It's unique for every user and is reset after 24 hours. It cannot be used to view the history of anyone else. It is fine to share this key with anyone you want, the only "private" data they will have access to is the wish history.Tip for developers: the first 682 characters (85 bytes) of the authkey are always the same for each user. You can use this to easily indentify if an authkey belongs to the same user as another authkey.How can I claim daily rewards for other users.When making projects that claim daily rewards for other users you can pass in acookieparameter -claim_daily_rewards(cookie={'ltuid': ..., 'ltoken': ...}).
This will avoid having to set global cookies so you can use it with threading.This cookie parameter is avalible for the majority of other accounts.Is it possible that my account can be stolen when I login with the cookie?I would like to be completely clear in this aspect, I do no have any way to access the cookies you use to login. If you give your cookie to someone it is indeed possible to get into your account but that doesn't yet mean they can do anything with it. The most probable thing a hacker would do is just do a password request, but since version1.3they will need to confirm this request with an email. That means they would need to know what your email is and have a way to get into it, which I doubt they can. Since version1.5there is also 2FA which will make it completely impossible to steal your account.They can of course access your data like email, phone number and real name, however those are censored so unless they already have an idea what those could be that data is useless to them. (For example the email may bethesadru@gmail.combut it'll only show up asth****ru@gmail.com)TL;DR unless you have also given your password away your account cannnot be stolen.How do I get the wish history of other players?To get the wish history of other players you must get their authkey and pass it as a keyword intoget_wish_history. That will make the function return their wish history instead of yours, it will also avoid the error when you try to run your project on a machine that doesn't have genshin installed.To get the autkey you ask the player to pressESCwhile in the game and click the feedback button on the bottom left, then get them to send the url they get redirected to. You can then extract the authkey withextract_authkey(url)which you can then pass into the functions.Why doesn'tget_wish_history()return a normal list?When you doprint(gs.get_wish_history())you get  an output that looks something like<generator object get_wish_history at 0x000001DA6A128580>This is because the wish history is split into pages which must be requested one at a time, that means trying to return all the pulls at once would take way too long. The wokaround around this is to use a "generator" - a special list that generates values on the fly.If you absolutely need a list you can just explicitely cast the generator to a list withlist(get_wish_history())however that might take a few seconds fetch.How doesset_cookie_auto()work? Can my data be stolen with it?set_cookie_auto()searches your browsers for possible cookies used to login into your genshin accounts and then uses those, so there's no need to useset_cookies().
When getting said cookies, they are filtered so only ones for mihoyo are ever pulled out. They will only ever be used as authentication and will never be sent anywhere else.What's the ratelimit?You may only fetch data for 30 users per day per cookie.
That means that when making projects that fetches data for other users it's recommended to use multiple cookies.
You can set a bunch of cookies at once withset_cookies(cookie1, cookie2, ...).How can I get an in-game uid from a hoyolab uid?get_uid_from_hoyolab(hoyolab_uid)can do that for you. It will return None if the user's data is private. To check whether a given uid is a game or a hoyolab one useis_game_uid(uid).How can I get a user's username?Getting the user's username and adventure rank is possible only with their hoyolab uid. You can get them withget_record_card(hoyolab_uid)along with a bunch of other data.How do I get one type of uid from another?uids of currently logged in user:gs.get_game_accounts()from hoyolab uid to game uid:gs.get_uid_from_hoyolab(hoyolab_uid)from authkey to uid:gs.get_uid_from_authkey(authkey)from uid to hoyolab uid:currently impossibleproject layoutcaching.py          caching utilities
daily.py            automatic daily reward claiming
errors.py           errors used by genshinstats
genshinstats.py     user stats and characters
hoyolab.py          user hoyolab community info
map.py              genshin webstatic map
pretty.py           formatting of api responses
transactions.py     transaction logs
utils.py            various utility functions
wishes.py           wish historyCHANGELOG1.4.11.3Added support for realm currency counter inget_notes1.4.11.2Make the record card return proper values for new accounts1.4.11.1Fix various chinese endpointsUpdate domain to account for hoyolab server migration1.4.11Addedget_notes1.4.10Added anequipmentkwarg toget_user_statsAdded support for activities1.4.9.4Made Ei and Sara have correct names in the abyss1.4.9.3Teapot now prettifies data properlyAloy no longer has 105 stars1.4.9.2Made sure that None returns do not get cached1.4.9.1Minor mypy fixes1.4.9Added support for the genshin mapMadedaily_reward_inforeturn a namedtuple1.4.8Fixed support for chinese accounts1.4.7Addedinstall_cachefor installing a cache into the entire library1.4.6.1Added an error for when you don't have a hoyolab account created1.4.6Added several new log related endpointsget_primogem_log, get_resin_log, get_crystal_logget_artifact_log, get_weapon_logMade tests cleaner (now requires pytest)Added an explanation how to get your authkey on other platforms1.4.5.1Made yanfei have the correct name in spiral abyss1.4.5Added support for the serenita teapotThis is not yet an official endpoint, therefore there's a large number of bugs. For example the comfort level is shared across all styles.Madeget_game_accountsbe prettifiedThis will most likely break a lot of scripts, I'm sorry in advance.Added aretrydecorator1.4.4.4Added support for electroculi and outfitsAnnotated all unsubscripted lists in the library asList[Dict[str, Any]]1.4.4.3Added electro traveler's element to the character prettifierAnnotated all unsubscripted dicts in the library asDict[str, Any]Fixed bug where getting banner types with different authkeys would not get the cached result1.4.4.2Added a custom error message for set_cookie_auto1.4.4.1Addedvalidate_authkeyMadeclaim_daily_rewardno longer require a uid.RenamedGachaLogExceptiontoAuthkeyErrorRemovedgenshinstats.asyncify()in favor ofasyncio.to_thread()1.4.4Added a cookie parameter to all functions that use cookiesAddedset_visibilityMadeclaim_daily_rewardsend an email in the correct languageImprovedsetup.pyUpdated to the new wish history domain1.4.3Added a cookie parameter toclaim_daily_reward1.4.2Made several improvements to the claiming of daily rewards1.4.1Addedget_recommended_usersAddedget_hot_posts1.4Renamed majority of functionsget_user_data->get_user_statsget_gacha_history->get_wish_historythe before ambigiousgachawas renamed towishorbannnersign_in->claim_daily_reward...Removedget_all_*style functions - functions are overloaded to get all by defaultMade it possible to use multiple cookies to overcome ratelimitsset_cookiekeeps its behaviour but is now overloaded to work with headersset_cookiessets multiple cookies which will be silently rotated as neededRemoved the need for short lang codes, they are now parsed internallyget_langsandget_banner_typesnow return a dict instead of a list of dictsGift codes can now be redeemed for all game accounts instead of just a single one.Added__all__to not spam the namespace with unexpected variablesReduced the amount of errors