Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Complete issue import API walkthrough with curl

This gist describes a new GitHub API for importing issues. This API hasn't been finalized yet, but when it is -- it will be announced on the official API blog.

Overview:

General notes

To access the Issue Import API, you'll need to include application/vnd.github.golden-comet-preview in your request's Accept header. You'll also need to have admin permissions on the repository.

The maximum request size is 1MB.

Issues and comments created with this API do not trigger any notifications to mentioned users or users watching the repository into which issues are being imported. This is unlike the Issues API which does trigger notifications).

Like all other API requests, requests made to the Issue Import API are affected by regular API rate limits and abuse rate limits.

Issue imports started with this API are processed in the background asynchronously. When you start an import, you get back an import URL which you can use to check if the import completed, and whether it completed successfully or not. Because of this background processing, issue numbers are handed out to successfully imported issues once the processing is completed, and not when you start an import. As a result, if you use this API to submit two imports for issues A and B, and B is submitted after A, but before A's import completes, then it is possible that B will have a lower issue number than A. If you need to make sure that issues have specific numbers, then the recommended approach is that you start an import, wait for that import to complete and make sure it completed successfully, and only after that start a new import.

If you have any feedback or questions about the Issue Import API, please get in touch.

Create a repository

Create a repository using the web interface or the API.

Here is an example API request to create a repository:

curl -X POST -H "Authorization: token ${GITHUB_TOKEN}" \
  -d '{"name":"foo","private":true}' \
  https://api.github.com/user/repos

Try to start an issue import with incomplete data

The Issue Import API attempts to provide actionable feedback when a request does not meet validation criteria.

Request

curl -X POST -H "Authorization: token ${GITHUB_TOKEN}" \
  -H "Accept: application/vnd.github.golden-comet-preview+json" \
  -d '{"issue":{"title":"My money, mo issues"}}' \
  https://api.github.com/repos/${GITHUB_USERNAME}/foo/import/issues

Response

{
  "message": "Validation Failed",
  "errors": [
    {
      "resource": "Issue",
      "code": "missing_field",
      "field": "body"
    }
  ],
  "documentation_url": "https://developer.github.com/v3"
}

Start an issue import

Just make a post request to the import issues API endpoint for the repository and include the issue and comments json as data. An issue only requires the following fields:

{
  "issue": {
    "title": "Imported from some other system",
    "body": "..."
  }
}

And an issue with a comment only needs the comment body added:

{
  "issue": {
    "title": "Imported from some other system",
    "body": "..."
  },
  "comments": [
    {
      "body": "talk talk"
    }
  ]
}

Request

curl -X POST -H "Authorization: token ${GITHUB_TOKEN}" \
  -H "Accept: application/vnd.github.golden-comet-preview+json" \
  -d '{"issue":{"title":"Fix broken widgets","body":"- [ ] widget 1\n- [ ] widget 2","created_at":"2014-03-16T18:21:16Z"},"comments":[{"body":"Can we wrap this up soon?","created_at":"2014-03-16T17:15:42Z"}]}' \
  https://api.github.com/repos/${GITHUB_USERNAME}/foo/import/issues

Response

{
  "id": 3,
  "status": "pending",
  "url": "https://api.github.com/repos/jonmagic/foo/import/issues/3",
  "import_issues_url": "https://api.github.com/repos/jonmagic/foo/import/issues",
  "repository_url": "https://api.github.com/repos/jonmagic/foo"
}

Supported issue and comment fields

The complete json schema looks like this:

{
  "issue": {
    "title": "Imported from some other system",
    "body": "...",
    "created_at": "2014-01-01T12:34:58Z",
    "closed_at": "2014-01-02T12:24:56Z",
    "updated_at": "2014-01-03T11:34:53Z",
    "assignee": "jonmagic",
    "milestone": 1,
    "closed": true,
    "labels": [
      "bug",
      "low"
    ]
  },
  "comments": [
    {
      "created_at": "2014-01-02T12:34:56Z",
      "body": "talk talk"
    }
  ]
}

Note: The body of an issue or comment will be parsed and rendered using Markdown if the created_at value is set to a time after 2009-04-20T19:00:00Z, and using Textile if it's older. The body may be at most 65535 characters long. As of 2016-03-15 all newly created issues will be rendered with Markdown regardless of the created_at timestamp date.

Check status of issue import

After making the API request to import an issue you can check the status of the import by making a get request to the url value from the response in the previous post request.

Request

curl -H "Authorization: token ${GITHUB_TOKEN}" \
  -H "Accept: application/vnd.github.golden-comet-preview+json" \
  https://api.github.com/repos/#{GITHUB_USERNAME}/foo/import/issues/3

Response

{
  "id": 3,
  "status": "imported",
  "url": "https://api.github.com/repos/jonmagic/foo/import/issues/3",
  "import_issues_url": "https://api.github.com/repos/jonmagic/foo/import/issues",
  "repository_url": "https://api.github.com/repos/jonmagic/foo",
  "issue_url": "https://api.github.com/repos/jonmagic/foo/issues/1"
}

Check status of multiple issues

You can check the status of issues imported since a date or date and time.

Request

curl -H "Authorization: token ${GITHUB_TOKEN}" \
  -H "Accept: application/vnd.github.golden-comet-preview+json" \
  https://api.github.com/repos/#{GITHUB_USERNAME}/foo/import/issues?since=2015-03-15

Response

[
  {
    "id": 3,
    "status": "imported",
    "url": "https://api.github.com/repos/jonmagic/foo/import/issues/3",
    "import_issues_url": "https://api.github.com/repos/jonmagic/foo/import/issues",
    "repository_url": "https://api.github.com/repos/jonmagic/foo",
    "issue_url": "https://api.github.com/repos/jonmagic/foo/issues/1"
  }
]

Import fails because issue has an invalid milestone, assignee, creator or label

What happens if you try to import an issue with an invalid attributes?

Import issue request

curl -X POST -H "Authorization: token ${GITHUB_TOKEN}" \
  -H "Accept: application/vnd.github.golden-comet-preview+json" \
  -d '{"issue":{"title":"Fix broken widgets","body":"...","milestone":5,
      "assignee":"bobbyfoobar","labels":["bug","invalid,label"]}}' \
  https://api.github.com/repos/${GITHUB_USERNAME}/foo/import/issues

Import issue response

{
  "id": 7,
  "status": "pending",
  "url": "https://api.github.com/repos/jonmagic/foo/import/issues/7",
  "import_issues_url": "https://api.github.com/repos/jonmagic/foo/import/issues",
  "repository_url": "https://api.github.com/repos/jonmagic/foo"
}

Import status request

curl -H "Authorization: token ${GITHUB_TOKEN}" \
  -H "Accept: application/vnd.github.golden-comet-preview+json" \
  https://api.github.com/repos/#{GITHUB_USERNAME}/foo/import/issues/7

Import status response

{
  "id": 7,
  "status": "failed",
  "url": "https://api.github.com/repos/jonmagic/foo/import/issues/7",
  "import_issues_url": "https://api.github.com/repos/jonmagic/foo/import/issues",
  "repository_url": "https://api.github.com/repos/jonmagic/foo",
  "created_at": "2015-03-18T19:40:53-07:00",
  "updated_at": "2015-03-18T19:40:58-07:00",
  "errors": [
    {
      "location": "/issue/milestone",
      "resource": "Issue",
      "field": "milestone",
      "value": "5",
      "code": "invalid"
    },
    {
      "location": "/issue/assignee",
      "resource": "Issue",
      "field": "assignee",
      "value": "bobbyfoobar",
      "code": "invalid"
    },
    {
      "location": "/issue/labels[1]",
      "resource": "Label",
      "field": "name",
      "value": "invalid,label",
      "code": "invalid"
    }
  ]
}

Import fails because of unexpected comment creation error

Import issue request

curl -X POST -H "Authorization: token ${GITHUB_TOKEN}" \
  -H "Accept: application/vnd.github.golden-comet-preview+json" \
  -d '{"issue":{"title":"foo","body":"1"},"comments":[{"body":"fail this comment somehow"}]}' \
  https://api.github.com/repos/${GITHUB_USERNAME}/foo/import/issues

Import issue response

{
  "id": 12,
  "status": "pending",
  "url": "https://api.github.com/repos/jonmagic/foo/import/issues/12",
  "import_issues_url": "https://api.github.com/repos/jonmagic/foo/import/issues",
  "repository_url": "https://api.github.com/repos/jonmagic/foo",
  "created_at": "2015-03-18T21:45:58-07:00",
}

Import status request

curl -H "Authorization: token ${GITHUB_TOKEN}" \
  -H "Accept: application/vnd.github.golden-comet-preview+json" \
  https://api.github.com/repos/#{GITHUB_USERNAME}/foo/import/issues/12

Import status response

{
  "id": 12,
  "status": "failed",
  "url": "https://api.github.com/repos/jonmagic/foo/import/issues/12",
  "import_issues_url": "https://api.github.com/repos/jonmagic/foo/import/issues",
  "repository_url": "https://api.github.com/repos/jonmagic/foo",
  "created_at": "2015-03-18T21:45:58-07:00",
  "updated_at": "2015-03-18T21:46:05-07:00",
  "issue_url": "https://api.github.com/repos/jonmagic/foo/issues/7",
  "errors": [
    {
      "location": "/comments[0]",
      "resource": "IssueComment",
      "field": null,
      "value": null,
      "code": "error"
    }
  ]
}
@jcoffland

This comment has been minimized.

Copy link

commented Apr 2, 2015

This is interesting. It's great that it allows you to keep the correct timestamps on comments and issues but what about status changes such as opening and closing the ticket or changing the title. This is also important context information. It would be great if the API's JSON schema also supported another field changes with contents like this:

"changes": [
  {
    "created_at": "2014-01-02T12:34:56Z",
    "change": "title",
    "value": "My new ticket title"
  },
  {
    "created_at": "2014-01-03T12:34:56Z",
    "change": "closed"
  },
  {
    "created_at": "2014-01-04T12:34:56Z",
    "change": "reopened"
  }
]

With this addition I could make a fairly complete conversion of my Trac tickets to GitHub issues.

@jcoffland

This comment has been minimized.

Copy link

commented Apr 2, 2015

Also, it would be great if you could specify issue ids.

@jcoffland

This comment has been minimized.

Copy link

commented Apr 3, 2015

There is some sort of length limit which I ran in to when uploading a very large issue. Limiting the size solved the problem. Could you clarify in the API what the limits are. The error returned was ambiguous. It had null as the field value.

@sebastienros

This comment has been minimized.

Copy link

commented Apr 12, 2015

I agree with @jcoffland on the missing changes. Most of our items are closed and it currently requires an additional standard call, PATCH on issues. Then I hit the api rate limits (>5K issues to import).
If not an array of changes, at least for me a closed_at.

@jonmagic

This comment has been minimized.

Copy link
Owner Author

commented Apr 14, 2015

Please direct feedback to support@github.com as gist does not have comment notifications. Thanks!

@kilianc

This comment has been minimized.

Copy link

commented Oct 29, 2015

+1 on closed_at.

Also a way to delete old issues

@agriffis

This comment has been minimized.

Copy link

commented Feb 18, 2016

The "complete json schema" doesn't include creator, but later there's mention of failing if the issue has an invalid creator. Is the creator something we can set using this API?

@brianoliver

This comment has been minimized.

Copy link

commented May 14, 2016

Just working with the new issues importing API and noticed that it’s not possible to set the author of a comment. All comments are imported using the username of the importer.

It would be great to be able to specify the author of a comment, like we can with the regular API.

@Zimmi48

This comment has been minimized.

Copy link

commented Oct 11, 2017

Testing this with:

$ curl -X POST -H "Authorization: token ${GITHUB_TOKEN}"   -H "Accept: application/vnd.github.golden-comet-preview+json"   -d '{"issue":{"title":"Fake issue","body":"Fake issue"}}'   https://api.github.com/repos/Zimmi48/test-issue-import-api/import/issues

gives me:

{
  "id": 1407016,
  "status": "pending",
  "url": "https://api.github.com/repos/Zimmi48/test-issue-import-api/import/issues/1407016",
  "import_issues_url": "https://api.github.com/repos/Zimmi48/test-issue-import-api/import/issues",
  "repository_url": "https://api.github.com/repos/Zimmi48/test-issue-import-api",
  "created_at": "2017-10-11T17:53:02+02:00",
  "updated_at": "2017-10-11T17:53:02+02:00"
}

But then

$ curl -H "Authorization: token ${GITHUB_TOKEN}"   -H "Accept: application/vnd.github.golden-comet-preview+json"  https://api.github.com/repos/Zimmi48/test-issue-import-api/import/issues/1407016

yields:

{
  "message": "Not Found",
  "documentation_url": "https://developer.github.com/v3"
}

and

$ curl -H "Authorization: token ${GITHUB_TOKEN}"   -H "Accept: application/vnd.github.golden-comet-preview+json"   https://api.github.com/repos/Zimmi48/test-issue-import-api/import/issues?since=2017-10-11

yields:

[

]

and sure enough: the issue is not created.

@Zimmi48

This comment has been minimized.

Copy link

commented Oct 11, 2017

The issue was finally added but with a very important delay.

@Zimmi48

This comment has been minimized.

Copy link

commented Oct 12, 2017

I don't know what was going on yesterday, but testing again now and this works perfectly and the API is very fast.

@ghost

This comment has been minimized.

Copy link

commented Oct 24, 2017

I experienced the same delay as well. Likely @github has some moderation.

@Zimmi48

This comment has been minimized.

Copy link

commented Oct 26, 2017

@xoviat: No when I experienced it, it was due to server problems on their side (at least this is what their support told me). Later I was able to import lots of issues using this API without any problems.

@zzzeek

This comment has been minimized.

Copy link

commented Feb 7, 2018

OK, this is bugging me. If i set an issue with closed-at and updated-at date, the labels still display as "zzzeek labeled the issue as foo just now". the issue was last updated in like 2006. Can this one little thing be fixed?

@DrewHood

This comment has been minimized.

Copy link

commented Mar 14, 2018

I noticed that the API will fail if I try to use a label on an issue that already exists in my project but isn’t an exact match for letter case. For example, Github’s default label “bug” conflicted with my pre-existing label “Bug” (note the capital B in mine). Deleting the lowercase default provided by Github allowed my issues with that label to be imported correctly. It’s a subtle thing but maybe the api should use a case-insensitive match?

@jeffwidman

This comment has been minimized.

Copy link

commented Jun 1, 2018

Please direct feedback to support@github.com as gist does not have comment notifications. Thanks!

Will do. But wanted to also post here in case anyone else knows offhand...

Is there a way to import attachments with this? They are supported in the GitHub web UI, just not seeing it in the API: jeffwidman/bitbucket-issue-migration#103

@AlexanderAmelkin

This comment has been minimized.

Copy link

commented Jun 7, 2018

@jonmagic, I've sent this to support@github.com, but either I'm having some problems with my e-mail, or my message got lost because I haven't got any (even automatic) reply. Here is my report:

I'm trying to migrate from SourceForge tickets to GitHub issues.
I'm using the method described at https://gist.github.com/jonmagic/5282384165e0f86ef105
and I'm having a problem with it. Some tickets just won't import although from the server's
response it looks like they should have succeeded.

Specifically, this request looks like successful:

curl -X POST -H "Authorization: token `cat token`" \
     -H "Accept: application/vnd.github.golden-comet-preview+json" \
     -d @foo.json \
     https://api.github.com/repos/ipmitool/test/import/issues
{
  "id": 1745386,
  "status": "pending",
  "url": "https://api.github.com/repos/ipmitool/test/import/issues/1745386",
  "import_issues_url": "https://api.github.com/repos/ipmitool/test/import/issues",
  "repository_url": "https://api.github.com/repos/ipmitool/test",
  "created_at": "2018-06-06T17:06:35.660+03:00",
  "updated_at": "2018-06-06T17:06:35.660+03:00"
}

However if you check that "url" from the response, you will get this:

{
  "message": "Not Found",
  "documentation_url": "https://gist.github.com/jonmagic/5282384165e0f86ef105#check-status-of-issue-import"
}

So far I have found out that it all depends on the contents of foo.json.
The problematic foo.json is available here.
Please help me find out what's wrong with this particular request.

Also, is it true that when using the issue import API I still should not reference
users with @ in the imported tickets to avoid spamming those users?
Or can I safely reference them and don't be afraid that my importing
script will get banned for spamming?

@giorgiosironi

This comment has been minimized.

Copy link

commented Jun 29, 2018

It would be great to be able to specify the author of a comment, like we can with the regular API.

As far as I know this isn't possible with the regular Issues and Issue Comments APIs: the author will be the owner of the authentication token being used. The problem in this API is then that the owner of the token will be used for both the issue and all comments, which practically limits you to use a fictional user created for this.

@semiaddict

This comment has been minimized.

Copy link

commented Sep 7, 2018

The problem in this API is then that the owner of the token will be used for both the issue and all comments, which practically limits you to use a fictional user created for this.

Has there been any evolution on this ?

@billybooth

This comment has been minimized.

Copy link

commented Oct 10, 2018

Just working with the new issues importing API and noticed that it’s not possible to set the author of a comment. All comments are imported using the username of the importer.

It would be great to be able to specify the author of a comment, like we can with the regular API.

Let me go ahead and third or fourth this. Given that the authorized user is an admin for the repo, even if comment authors should be limited to real accounts and not fictionalized, why not require access tokens for any accounts to be posted under and allow the specification of a comment author and token? Or at least allow the contribution of comments with dates for imported issues by additional requests using this API. This limitation basically forces us to utilize the standard Issues API for migrating issues, where we lose our original dates but maintain our team’s commenter/creator identities. With the “import” API, we can preserve dates but we lose our identities. It’s a bit frustrating.

@stmllr

This comment has been minimized.

Copy link

commented Jun 5, 2019

UPDATE: Fixed

It seems the API is currently broken when importing a closed issue.

Importing closed issue: error

echo '{ "issue": { "title": "foo", "body": "bar", "closed": true } }' > issue.json
curl -X POST -H "Authorization: token ${token}" -H "Accept: application/vnd.github.golden-comet-preview+json" -d @issue.json https://api.github.com/repositories/190352707/import/issues
{
  "id": 2453079,
  "status": "pending",
  "url": "https://api.github.com/repos/stmllr/test-restored-6625/import/issues/2453079",
  "import_issues_url": "https://api.github.com/repos/stmllr/test-restored-6625/import/issues",
  "repository_url": "https://api.github.com/repos/stmllr/test-restored-6625",
  "created_at": "2019-06-05T09:13:24.000Z",
  "updated_at": "2019-06-05T09:13:24.000Z"
}
curl -H GET -H "Authorization: token ${token}" -H "Accept: application/vnd.github.golden-comet-preview+json" https://api.github.com/repositories/190352707/import/issues/2453079
{
  "id": 2453079,
  "status": "failed",
  "url": "https://api.github.com/repos/stmllr/test-restored-6625/import/issues/2453079",
  "import_issues_url": "https://api.github.com/repos/stmllr/test-restored-6625/import/issues",
  "repository_url": "https://api.github.com/repos/stmllr/test-restored-6625",
  "created_at": "2019-06-05T09:13:24.000Z",
  "updated_at": "2019-06-05T09:13:24.000Z",
  "issue_url": "https://api.github.com/repos/stmllr/test-restored-6625/issues/15",
  "errors": [
    {
      "location": "/issue",
      "resource": "Issue",
      "field": null,
      "value": null,
      "code": "error"
    }
  ]
}

The issue was created in the target repository, but state is open and status check returns "error".
Setting closed_at date did not change the behavior.

Importing open issue

If I set closed to false in the import issue, status check has no errors:

echo '{ "issue": { "title": "foo", "body": "bar", "closed": false } }' > issue.json
curl -X POST -H "Authorization: token ${token}" -H "Accept: application/vnd.github.golden-comet-preview+json" -d @issue.json https://api.github.com/repositories/190352707/import/issues
{
  "id": 2453082,
  "status": "pending",
  "url": "https://api.github.com/repos/stmllr/test-restored-6625/import/issues/2453082",
  "import_issues_url": "https://api.github.com/repos/stmllr/test-restored-6625/import/issues",
  "repository_url": "https://api.github.com/repos/stmllr/test-restored-6625",
  "created_at": "2019-06-05T09:19:13.000Z",
  "updated_at": "2019-06-05T09:19:13.000Z"
}
curl -H GET -H "Authorization: token ${token}" -H "Accept: application/vnd.github.golden-comet-preview+json" https://api.github.com/repositories/190352707/import/issues/2453082
{
  "id": 2453082,
  "status": "imported",
  "url": "https://api.github.com/repos/stmllr/test-restored-6625/import/issues/2453082",
  "import_issues_url": "https://api.github.com/repos/stmllr/test-restored-6625/import/issues",
  "repository_url": "https://api.github.com/repos/stmllr/test-restored-6625",
  "created_at": "2019-06-05T09:19:13.000Z",
  "updated_at": "2019-06-05T09:19:13.000Z",
  "issue_url": "https://api.github.com/repos/stmllr/test-restored-6625/issues/16"
}
@toddr

This comment has been minimized.

Copy link

commented Oct 9, 2019

BUG: If you add labels to the import, the time stamp on the event is now. This doesn't reflect last modified luckily but ideally the time stamp would conform to the creation date of the case or something?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.