# Ghost admin api v2

**URL:** <https://forum.ghost.org/t/ghost-admin-api-v2/8996>\
**Category:** Developer help\
**Created:** [September 10, 2019, 4:28pm UTC](https://forum.ghost.org/t/ghost-admin-api-v2/8996 "2019-09-10T16:28:47Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![JacquesBopp](https://avatars.discourse-cdn.com/v4/letter/j/f05b48/32.png) [@JacquesBopp](https://forum.ghost.org/u/JacquesBopp)\
**Post date:** [September 10, 2019, 4:28pm UTC](https://forum.ghost.org/t/ghost-admin-api-v2/8996/1 "2019-09-10T16:28:47Z")

</div>

Hi,  
I try to use the v2 api with python 3.7. I use the script below to create a post but I always get the error ‘Invalid token’. Tried many things but with no success. What’s wrong with my code?  
Kind regards,  
Jacques Bopp

```auto
import json
import requests
import jwt	# https://pyjwt.readthedocs.io
import datetime

key = 'my:admin_key' # Admin API keys can be obtained by creating a new Custom Integration
id, secret = key.split(':') *# Split the API key by the : into an id and a secret*
secret = bytes.fromhex(secret) # Decode the hexadecimal secret into the original binary byte array
now = int(datetime.datetime.now().timestamp())
payload = {'iat': now,'exp': now+(5*60),'aud': '/v2/admin/'} # Timestamps are seconds sine the unix epoch: 'iat': 'now', 'exp': Max 5 minutes after 'now'
token = jwt.encode({'payload': payload}, secret, algorithm='HS256', headers={'alg': 'HS256', 'kid': id, 'typ': 'JWT'})

url = 'https://mydomain.com/ghost/api/v2/admin/posts/'
body = {'posts': [{'title': 'Hello World'}]}
headers = {'Authorization': 'Ghost {}'.format(token)}
r = requests.post(url, data=json.dumps(body), headers=headers)

# r.content = b'{"errors":[{"message":"Invalid token","context":null,"type":"BadRequestError","details":null,"property":null,"help":null,"code":"INVALID_JWT","id":"b77efe10-d3e7-11e9-b529-c11e6c33226b"}]}'

```

---

<div class="post-metadata">

**Author:** ![Hannah](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.ghost.org/hannah/32/22_2.png) [@Hannah](https://forum.ghost.org/u/Hannah)\
**Post date:** [September 11, 2019, 8:44am UTC](https://forum.ghost.org/t/ghost-admin-api-v2/8996/2 "2019-09-11T08:44:24Z")

</div>

Was very close to working, have fixed the issues and included below:

1. payload should be given to jwt.encode directly, not as an object
2. token is a python3 byte string, so the authorization header was was being sent as `Ghost b'long-token-here'`. Calling token.decode() fixes this.
3. Once the token was fixed it became clear requests wasn’t sending the data as json. One solution was to add a “content-type: application/json” header, but more elegant is to remove the json lib and use requests built-in ability to serialize json.

```auto
import requests
import jwt	# https://pyjwt.readthedocs.io
import datetime

key = 'my:admin_key' # Admin API keys can be obtained by creating a new Custom Integration
id, secret = key.split(':') # Split the API key by the : into an id and a secret*
secret = bytes.fromhex(secret) # Decode the hexadecimal secret into the original binary byte array
now = int(datetime.datetime.now().timestamp())
payload = {'iat': now,'exp': now+(5*60),'aud': '/v2/admin/'} # Timestamps are seconds sine the unix epoch: 'iat': 'now', 'exp': Max 5 minutes after 'now'
token = jwt.encode(payload, secret, algorithm='HS256', headers={'alg': 'HS256', 'kid': id, 'typ': 'JWT'})

url = 'http://localhost:2368/ghost/api/v2/admin/posts/'
body = {'posts': [{'title': 'Hello World'}]}
headers = {'Authorization': 'Ghost {}'.format(token.decode())}
r = requests.post(url, json=body, headers=headers)

print(r)

```

I’d like to get this example added to our API docs 🙂

---

<div class="post-metadata">

**Author:** ![JacquesBopp](https://avatars.discourse-cdn.com/v4/letter/j/f05b48/32.png) [@JacquesBopp](https://forum.ghost.org/u/JacquesBopp)\
**Post date:** [September 11, 2019, 2:11pm UTC](https://forum.ghost.org/t/ghost-admin-api-v2/8996/3 "2019-09-11T14:11:03Z")

</div>

> [@Hannah](#):
>
> key = ‘my:admin\_key’ # Admin API keys can be obtained by creating a new Custom Integration id, secret = key.split(‘:’) # Split the API key by the : into an id and a secret\* secret = bytes.fromhex(secret) # Decode the hexadecimal secret into the original binary byte array now = int(datetime.datetime.now().timestamp()) payload = {‘iat’: now,‘exp’: now+(5\*60),‘aud’: ‘/v2/admin/’} # Timestamps are seconds sine the unix epoch: ‘iat’: ‘now’, ‘exp’: Max 5 minutes after ‘now’ token = jwt.encode(payload, secret, algorithm=‘HS256’, headers={‘alg’: ‘HS256’, ‘kid’: id, ‘typ’: ‘JWT’}) url = ‘[http://localhost:2368/ghost/api/v2/admin/posts/](http://localhost:2368/ghost/api/v2/admin/posts/)’ body = {‘posts’: [{‘title’: ‘Hello World’}]} headers = {‘Authorization’: ‘Ghost {}’.format(token.decode())} r = requests.post(url, json=body, headers=headers) print(r)

Hi Hannah,  
Works great. Thanks! A Python example will be a worthy addition to the api docs.  
Jacques

---

<div class="post-metadata">

**Author:** ![adampwright](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.ghost.org/adampwright/32/10018_2.png) [@adampwright](https://forum.ghost.org/u/adampwright)\
**Post date:** [October 21, 2020, 6:57pm UTC](https://forum.ghost.org/t/ghost-admin-api-v2/8996/4 "2020-10-21T18:57:41Z")

</div>

Hannah, I tried your solution to a separate issue but get the following error:  
File “jwt.py”, line 5  
key = ‘’  
SyntaxError: invalid syntax

It’s a direct copy and paste from Ghost admin backend. Any ideas?

---

<div class="post-metadata">

**Author:** ![Kevin](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.ghost.org/kevin/32/32_2.png) [@Kevin](https://forum.ghost.org/u/Kevin)\
**Post date:** [October 21, 2020, 7:06pm UTC](https://forum.ghost.org/t/ghost-admin-api-v2/8996/5 "2020-10-21T19:06:05Z")

</div>

> [@adampwright](#):
>
> SyntaxError: invalid syntax

Sounds like something is wrong with the code syntax rather than the key. Have you checked that the correct quote symbols are being used around the string?
