Bad request using Admin API to post

Hi fellow ghost users

I have students writing articles for a school paper. They use another CMS to produce a print version. I’d like to automatically publish their articles to a ghost blog using the Admin API with python.

I was able to publish articles under my name (as ghost blog owner) using the API. But I would prefer students get credit.

Here is what I do. After authorizing with jwt, I open a session with a given student’s credentials.

url = ''
headers = {'Authorization': 'Ghost {}'.format(jeton.decode()),"Origin": ""}
estudiante = {
    "email": "",
    "password": whatever
r =, json=estudiante, headers=headers)

# I record the session cookie to file with pickle

with open("biscuits.pkl", "wb") as oreo:
    pickle.dump(r.cookies, oreo)

The Ghost blog returns a 201 as status code, meaning success. The response headers include the session cookie, ending with «Hb4»:

{'Server': 'nginx/1.14.0 (Ubuntu)', 'Date': 'Mon, 09 Mar 2020 04:12:54 GMT', 'Content-Type': 'text/plain; charset=utf-8', 'Content-Length': '7', 'Connection': 'keep-alive', 'X-Powered-By': 'Express', 'Cache-Control': 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0', 'Access-Control-Allow-Origin': '', 'Vary': 'Origin, Accept-Encoding', 'ETag': 'W/"7-rM9AyJuqT6iOan/xHh+AW+7K/T8"', 'Set-Cookie': 'ghost-admin-api-session=s%3AZbe-lLt4rgGJ-FZs1S0pFUeDhbcDF-s4.%2Fd8H0M4pwDTaJG94Xm5SJVap%2FQWpns2fETzcgxYfHb4; Path=/ghost; Expires=Mon, 07 Sep 2020 16:12:54 GMT; HttpOnly; Secure; SameSite=Lax', 'Strict-Transport-Security': 'max-age=63072000; includeSubDomains; preload', 'X-Frame-Options': 'SAMEORIGIN', 'X-Content-Type-Options': 'nosniff'}

Using pickle, I include the cookie in a POST request to publish the student’s post:

url = "", "Origin":""}
headers = {"Authorization": "Ghost {}".format(jeton.decode()), "Credentials":"include", "Origin":""}
body = {"posts": [{
	"title": "Est-ce que le COVID-19 menace votre porte-monnaie?",
	"authors": [estudiante["email"]],
	"html": '<p >La Banque centrale américaine a baissé son taux directeur [...] à moins d\'une faillite.</p>',
	"status": "published"

with open("biscuits.pkl", "rb") as goglu:
    biscuits = pickle.load(goglu)

r2 =, json=body, headers=headers, cookies=biscuits)

The headers of this second request show the cookie, ending with «Hb4», was passed along:

{'User-Agent': 'python-requests/2.22.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Ghost <ChuckNorrisToldMeNotToDiscloseOrElse>', 'Credentials': 'include', 'Origin': '', 'Cookie': 'ghost-admin-api-session=s%3AZbe-lLt4rgGJ-FZs1S0pFUeDhbcDF-s4.%2Fd8H0M4pwDTaJG94Xm5SJVap%2FQWpns2fETzcgxYfHb4', 'Content-Length': '4212', 'Content-Type': 'application/json'}

Yet, I get a 400 error (Bad Request). What am I doing wrong?


JH Roy

headers = {"Authorization": "Ghost {}".format(jeton.decode())

You shouldn’t be supplying the Authorization header if you are using session/cookie authentication, the authorisation header is used for JWT authentication. You need to make the request with the cookie you retrieved in the first step but no other authentication headers are needed.

Alternatively, if you use Token authentication instead then you can create posts as any staff user you want, you don’t need to use student’s usernames+passwords to log in as their user.

Docs for both auth methods are here:


Thank you very much, Kevin!

In fact, I was using JWT authentication, but was getting an HTTP error as soon as I wasn’t publishing as myself. That is why I tried the sessions/cookie way…

I now realize it was simply a matter of putting the author’s email between brackets. I wasn’t doing so in my first attempts with JWT authentication.

If I may suggest, this sentence in Admin API docs, which are otherwise very helpful:

«Short form uses a single string to identify a tag or author resource. Tags are identified by name and authors are identified by email address:»

could be improved thus:

«Short form uses a single string to identify a tag or author resource. Tags are identified by name and authors are identified by a list of email address:»

Thanks again and, mostly, thanks a lot for ghost! :smile: :+1: :100:

1 Like