Skip to content

Instantly share code, notes, and snippets.

@towerofnix
Last active August 25, 2020 15:23
Show Gist options
  • Save towerofnix/5c75560dfea08f8cda54d0a459c2085f to your computer and use it in GitHub Desktop.
Save towerofnix/5c75560dfea08f8cda54d0a459c2085f to your computer and use it in GitHub Desktop.
Excerpt from WIP Scratch API guide

Working with Projects

Projects, of course, comprise the bulk of Scratch. These endpoints provide the up-to-date ways of interacting with projects, as used by the migrated project page.

Fetching a project

There are two components that make up the identity of a project: its data, which is the actual project file, and its details, which is information like who made it, its notes and credits, and its current love-it/favorite counts. Of course you'll want the first if you're working with the actual programs represented by projects. Unlike most parts of the Scratch API, you don't actually use https://api.scratch.mit.edu to download projects - instead you use a separate domain, https://projects.scratch.mit.edu:

> GET https://projects.scratch.mit.edu/276660597
< {"targets":[{"isStage":true,"name":"Stage", ...

As shown in the example above, using this endpoint yields the project.json file that represents the project - not the actual .sb2 or .sb3 that you would download from inside the Scratch editor. (The exception to that rule is Scratch 1.4 projects, which do return the entire project binary.) While that file may be enough for your own use case, if you want to fetch the costumes and sounds used in the project, you'll need to make additional requests, this time to https://assets.scratch.mit.edu:

> GET https://assets.scratch.mit.edu/318125267295c8b3ec257bf7f81c6fd3.svg
< <svg xmlns="http://www.w3.org/2000/svg" ...

The ID you use there is included in the project.json fetched from projects.scratch.mit.edu. Exactly how that value is specified depends on whether the project you're analyzing was made in Scratch 3.0 or 2.0:

  • Scratch 3.0: Look for the md5ext property on asset objects. This is also equal to {assetId}.{dataFormat}.
  • Scratch 2.0: It depends on whether the asset is a sound or a costume:
    • Sounds: Use the md5 property.
    • Costumes (and backdrops): Most of the time you can use the baseLayerMD5 property, but if the costume was converted from opening a Scratch 1.4 project in 2.0, it might also include a textLayerMD5 property. If so, you'll get the most accurate image by fetching both of those assets and overlaying the image of textLayerMD5 on top of that of baseLayerMD5.

Since Scratch 1.4 projects are served as the full binary, including any costumes and sounds, you'll never have to fetch their assets individually. (You wouldn't be able to, anyway, since they aren't individually stored on the Scratch servers.)

Now if you just want the project's details, there's an ordinary API endpoint for that:

> https://api.scratch.mit.edu/projects/276660597
< {"id":"276660597",""title":"3.0h My Floss!" ...

The project object

The /projects/<id> endpoint returns a project object, which takes the following form:

{
    id: 276660597,
    title: "3.0h My Floss!",
    description: "Thanks to @champ99 and @mres for joining me..."
    instructions: "Ahhhh! A new year is here and so is Scratch 3.0 :D..."

    visibility: "visible"
    public: true
    comments_allowed: false
    is_published: true

    author: {...}

    image: "https://cdn2.scratch.mit.edu/get_image/project/276660597_480x360.png"
    images: {
        282x218: "https://cdn2.scratch.mit.edu/get_image/project/276660597_282x218.png?v=1546617266"
        216x163: ...
        ...
    }

    history: {
        created: "2019-01-02T18:38:37.000Z"
        modified: "2019-01-04T15:54:26.000Z"
        shared: "2019-01-02T19:55:46.000Z"
    }

    stats: {
        views: 15510
        loves: 1109
        favorites: 737
        comments: 1177
        remixes: 123
    }

    remix: {
        parent: 276664853
        root: 276660597
    }
}

(Well, those values in remix aren't actually for "3.0h My Floss!". They're for a different project, 3.0h My Floss! remix remix. :))

Most of those properties are self-descriptive, but not all, so here's a quick overview of some of the less obvious options:

  • author: This is the author of the project, and it's (almost) a whole user object. See the page Working with Users. The only properties that are missing are profile.id, profile.bio, and profile.country.
  • description: This is the text contents in "Notes and Credits"; instructions is the text in "Instructions".
  • image, images: These are full URLs to the thumbnails for the project. image refers to the highest-resolution file; the options in images are all smaller versions of the same image.
  • remix: The parent property is the ID of the project that this project was remixed off of; root is the original project in the "remix tree". If this project is a remix of that first project, parent and root will have the same value.
  • is_published: This boolean tells if the author of the project has shared it or not.
  • public: This property is more complex but basically is a combination of is_published and moderation checks, like whether the project's been censored or if the author's account has been deleted.
  • visibility: This is sort of a boolean telling if the project is in the trash section of the author's "My Stuff" page; it takes the values "visible" and "notvisible", though, not true and false.

Checking whether a project is censored

There's also an endpoint specifically for getting information about whether a project is "censored" or not - /users/<author>/projects/<id>/visibility:

> GET https://api.scratch.mit.edu/users/_nix/projects/200903024/visibility?x-token=...
< {"projectId":277771344,"deleted":false,...

Do note that the request requires authorization!

That object takes the following form:

{
    projectId: 277771344
    deleted: false
    censored: false
    reshareable: true
    message: ""
}

A project being censored means that it's been totally hidden from the community, usually as the result of a Scratch Team member responding to the project being reported. The message property includes the text they wrote, which may include telling you to make changes before re-sharing the project - whether you can do that is specified by the reshareable property.

The deleted property doesn't have to do with censorship; it just means whether or not the project is in the trash section of the author's "My Stuff".

Uploading a project

Uploading a project means submitting its project.json file to https://projects.scratch.mit.edu and submitting each asset to https://assets.scratch.mit.edu one by one.

To upload the project.json, make a request to https://projects.scratch.mit.edu that matches this description:

  • The method/URL is POST https://projects.scratch.mit.edu.
  • The Origin header is https://scratch.mit.edu. (Actually, you don't need to set this; it just means that, because of CORS, you won't be able to make your own website upload projects from its JS code.)
  • The Content-Type header is application/json.
  • The Cookie header contains your CSRF token and session ID (i.e. it follows the format scratchcsrftoken={...};scratchsessionsid={...}).
  • The body of the request is the project.json file's contents.

Upon doing so, you'll get a 200 OK response that looks something like this:

{
    status: "ok"
    content-name: "277765566"
    content-title: "VW50aXRsZWQtMjgy"
    autosave-interval: "120"
}

The main thing you'll find use of here is content-name, which is the ID of the project.

If you want to update a project.json, rather than upload a new one, just tweak your request's method to PUT and URL to https://projects.scratch.mit.edu/<id>. It'll respond with something like this:

{
    status: "ok"
    autosave-interval: "120"
}

Uploading project assets

Before uploading a project asset, you'll need to know its MD5 (and file extension); whatever programming language you're using most likely has a utility for getting the MD5 of a file's contents. Once you have that, make a request according to the following:

  • The method/URL is POST https://assets.scratch.mit.edu/<md5>.<ext>, where md5 is the MD5 of the file's contents and ext is its file extension. (Don't even think about sending the wrong MD5 - the server checks! :))
  • The Origin header is https://scratch.mit.edu. (Again, you don't actually need to set this.)
  • The Cookie header contains your CSRF token and session ID (just like when uploading a project).
  • The body of the request is the uploaded file's contents.

If the upload was successful, you'll get a 200 OK response that looks like this:

{
    status: "ok"
    content-name: "c4e9e84fd9244ca43986c2bdb6669ae8.wav"
}

If your upload is greater than Scratch's file size limit, you'll run into a 413 Request Entity Too Large error with the following content:

{
    code: "PayloadTooLarge"
    message: ""
}

There isn't really a concept of updating an already-uploaded asset; you'll just have to upload the file as though it's a totally new asset and update the MD5 referenced in your project.json accordingly.

Interacting with a project

Even once shared, Scratch projects aren't necessarily static things. There are several ways to interact with projects.

Love and favorite a project

To mark a project as loved or favorited, you'll want to send a simple POST request to one of these URLs:

  • https://api.scratch.mit.edu/proxy/projects/<id>/loves/user/<your-username>
  • https://api.scratch.mit.edu/proxy/projects/<id>/favorites/user/<your-username>

Of course, these endpoints require authorization.

Since these are /proxy endpoints, you'll need to specify your CSRF token via both cookie and X-CSRFToken header as well as your session ID via cookie. (There are also equivalent endpoints without the /proxy/ part, but because of a bug in the Scratch API, these don't send the project creator a notification telling that their project was loved.)

In response you'll get a value like this:

{
    projectId: 276660597
    userLove: true  // or userFavorite
    statusChanged: true
}

(The statusChanged property is always true, even if you'd already loved/favorited the project.)

Checking whether you've already loved or favorited a project is just as simple: just make a GET request to the same URL (with authorization; also, without the /proxy/ part).

To *un-*love/favorite a project, follow the exact same procedure as adding a love/favorite, but with the DELETE HTTP method, instead of POST.

Add a project to a studio

To add a project to a studio, just make a POST request to a URL in the format https://api.scratch.mit.edu/studios/<studio-id>/project/<project-id> (with authorization). Removing a project is equally simple - just use the DELETE HTTP method instead of POST. In response to a POST, you'll get an object like this:

{
    actorId: "14792872"
    projectId: "276660597"
    datetimeCreated: "2019-01-09 22:25:12"
    studioId: "5542140
}

Making a DELETE request just responds an empty array: [].

In either case, what you're looking for is a 200 OK status code. If you don't have permission to add to that studio, you'll get a 403 Forbidden error. (Same with removing a project from a studio - unless you're removing your own project, which is always allowed, even if you don't curate the studio.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment