Backing up your knowledge base with the Zendesk API

Have more questions? Submit a request


  • ccaux

    Hi, I am trying to do exactly the same thing as Howie (exactly same use case). 

    I have started a reference doc to map IDs for Category and Sections and have created those in my Sandbox. 

    Is there a script available to recreate all the articles? I understand that the newly create articles will have new IDs. 

    Alternatively is there a recommended process for major overalls of the Help Centre (new IA, new design etc..)? 


  • Bryan Flynn

    Hey there @ccaux. As this article's code is basically open-source, perhaps someone else in the community has added this feature and will share. Other than that, the article was really for demo purposes and the code is not actively updated.

    As for customizing Help Center (HC), the community has a lot more content and activity around HC cutomizations and ideas. There are also knowledge base articles there on the subject, too. The site is more focused on APIs, Apps framework, and Embeddables (widgets & Mobile SDKs). I know this is a delayed response but hope it helps all the same.

  • Travis Abdelhamed

    When creating the log file using the provided code, your file will have a blank row between every article when opening in windows. To eliminate the blank row, modify the following line accordingly (adding the newline parameter):


    with open(os.path.join(backup_path, '_log.csv'), mode='wt', newline='', encoding='utf-8') as f:



  • Nikolaus Thumma

    Hey Charles - I'm running into an auth issue when trying to connect through the python script.  Does this not work if SSO is enabled?


    Thank You

  • Charles Nadeau

    Hi Jasper,

    There are 3 authentication options for using the API:

    • basic auth credentials using a Zendesk username & password
    • a Zendesk API token
    • an OAuth token created using a Zendesk API client

    For details, see



  • Dan Derks

    Hi Charles!

    Wonderful script, thank you -- we're all backed up.

    quick note: my machine has Python 2 and Python 3, so the pip commands defaulted to my Python 2 installation. perhaps it'd help others to explicitly use pip3?:

    $ pip3 install beautifulsoup4
    $ pip3 install lxml

    Re: Creating an Article with the Restore script, could you please walk a novice through which lines would need changed and what additional info is necessary?

    Thank you!

  • Rachel M


    I'm stuck on the credentials bit. We use an SSO, so I went the API token route. However, I can't seem to reference it a way that makes everyone happy. 

    I've tried:

    credentials = (base-64-encoded username/token:API_TOKEN_HERE)
    credentials = Authorization: "Basic (base-64-encoded username/token:API_TOKEN_HERE)"
    credentials = Authorization: 'Basic (base-64-encoded username/token:API_TOKEN_HERE)'
    session.auth = Authorization: "Basic (base-64-encoded username/token:API_TOKEN_HERE)"

    etc, and I keep receiving either a "SyntaxError: invalid syntax" or "TypeError: 'str' object is not callable" error. I think I'm missing something rather obvious. Any help would be appreciated!




  • Matt McLean


    Try this:

    import requests

    credentials = 'your_zendesk_email/token', 'your_token'
    zendesk = ''
    language = 'some_locale'


    Hope this helps.

  • Rachel M

    Hi Matt! 

    Ah – that worked! Thank you!

    So it ran. And it only output a .csv – which only contained the column headers, and no actual info. But now I'm closer. 

    Any thoughts? Thanks again!



    date =
    backup_path = os.path.join(str(date), language)
    if not os.path.exists(backup_path):

    log = []

    endpoint = zendesk + '/api/v2/help_center/us-en/articles.json'.format(locale=language.lower())
    while endpoint:
    response = session.get(endpoint)
    if response.status_code != 200:
    print('Failed to retrieve articles with error {}'.format(response.status_code))
    data = response.json()

    for article in data['articles']:
    if article['body'] is None:
    title = '<h1>' + article['title'] + '</h1>'
    filename = '{id}.html'.format(id=article['id'])
    with open(os.path.join(backup_path, filename), mode='w', encoding='utf-8') as f:
    f.write(title + '\n' + article['body'])
    print('{id} copied!'.format(id=article['id']))

    log.append((filename, article['title'], article['author_id']))

    endpoint = data['next_page']

    with open(os.path.join(backup_path, '_log.csv'), mode='wt', encoding='utf-8') as f:
    writer = csv.writer(f)
    writer.writerow(('File', 'Title', 'Author ID'))
    for article in log:
  • Rachel M

    eep! it worked!

    figured out what was wrong. I had "us-en" as opposed to "en-us". so silly – but i'm so happy it's working!



  • Bryan Flynn

    Thanks Matt for stepping in and thanks Rachel for sharing what the fix was -- quite easy for these small things to sneak in and not obvious!

  • Nicolas

    Hi Charles,


    Thanks for the great script.

    I am facing an issue, though: the script doesn't retrieve all articles.

    In my case, I manage to get 59 articles back while the Guide Admin Portal mentions 111 published, 4 drafts and 34 archived.

    I have checked that:

    - the superuser credentials I use allow me full access on frontend, to all user-segmented sections;

    - all articles are written in the same language.

    Only articles visible visible to everyone have been retrieved, not the one belonging to the user-segmented sections.

    Any idea about how to solve this?




  • Russell Dunn

    Thank you for the script!

    Echoing a few earlier comments however, I'm also struggling to retrieve all articles. I get 155 articles exported, but have 173 published articles. I am an administrator.

    We do have some articles that are only visible to Agents/managers, as per Nicolas' comment above, would that be the problem? How then would I also retrieve these articles?

  • Devan La Spisa

    Hello Russell,

    So this usually has something to do with the HC view permissions of the person making the API request (as identified by the credentials used to authenticate the request). If the person making the API request can't view certain articles in the HC (as specified by the view permissions in Guide), then the API won't return those articles to the person.

    Hope this helps to clarify things a bit better and let us know if there is anything else we can assist with please let us know. 

  • Russell Dunn

    Well yes thats what I thought, but when I said administrator the correct terminology in Guide is Manager, which is what my account is. I'm passing in my credentials as well, not running anonymously.

  • Devan La Spisa

    Hello Russel,

    The only other thing that might apply in the instance is the API credentials the being using. Are you using your own email/password credentials or an API token with your email? If using an API token, the request would be restricted to the permissions of the admin who **created** the API token initially, not the person making the request regardless of whether the requester is a Guide Manager.

    If that not the issue, then the next step would be to examine the 18 articles that are not returned to look for a common denominator.

  • Alan Oehler

    I'm having a strange problem running my backup script. It worked fine about 18 months ago when I last used it. Now I am getting this error:

    Traceback (most recent call last):

      File "", line 37, in <module>

        with open(os.path.join(backup_path, filename), mode='w', encoding='utf-8') as f:

    TypeError: 'encoding' is an invalid keyword argument for this function

    Anyone have an idea?

  • Robert Renda

    Alan --

    I'm running the same code/same syntax:

                with open(os.path.join(backup_path, filename), mode='w', encoding='utf-8') as f:
                    f.write(title + '\n' + category['name'] + category['description'])
    It has to be something else than that line you posted. TypeErrors are typically caused by combining objects of the wrong type, or calling a function with the wrong type of object.

Please sign in to leave a comment.

Powered by Zendesk