diff --git a/examples/using-medium/.eslintrc b/examples/using-medium/.eslintrc
new file mode 100644
index 0000000000000..d1e4cdd12970e
--- /dev/null
+++ b/examples/using-medium/.eslintrc
@@ -0,0 +1,8 @@
+{
+  "env": {
+    "browser": true
+  },
+  "globals": {
+    "graphql": false
+  }
+}
diff --git a/examples/using-medium/README.md b/examples/using-medium/README.md
new file mode 100644
index 0000000000000..b197f1131a2f7
--- /dev/null
+++ b/examples/using-medium/README.md
@@ -0,0 +1,5 @@
+# Using Medium
+
+https://using-medium.gatsbyjs.org
+
+Gatsby example site that shows how to use the gatsby-source-medium plugin.
diff --git a/examples/using-medium/gatsby-config.js b/examples/using-medium/gatsby-config.js
new file mode 100644
index 0000000000000..9eeeb13abc69c
--- /dev/null
+++ b/examples/using-medium/gatsby-config.js
@@ -0,0 +1,13 @@
+module.exports = {
+  siteMetadata: {
+    title: `Using Medium`,
+  },
+  plugins: [
+    {
+      resolve: `gatsby-source-medium`,
+      options: {
+        username: `smartive`,
+      },
+    },
+  ],
+}
diff --git a/examples/using-medium/package.json b/examples/using-medium/package.json
new file mode 100644
index 0000000000000..f08401e6d1d4d
--- /dev/null
+++ b/examples/using-medium/package.json
@@ -0,0 +1,18 @@
+{
+  "name": "gatsby-example-using-medmium",
+  "private": true,
+  "version": "1.0.0",
+  "description": "Gatsby example site that shows how to use the Medium source plugin",
+  "main": "index.js",
+  "author": "Robert Vogt <robert@smartive.ch>",
+  "license": "MIT",
+  "dependencies": {
+    "gatsby": "latest",
+    "gatsby-link": "latest"
+  },
+  "keywords": ["gatsby"],
+  "scripts": {
+    "develop": "gatsby develop",
+    "build": "gatsby build"
+  }
+}
diff --git a/examples/using-medium/src/layouts/index.js b/examples/using-medium/src/layouts/index.js
new file mode 100644
index 0000000000000..e38c6208493f0
--- /dev/null
+++ b/examples/using-medium/src/layouts/index.js
@@ -0,0 +1,13 @@
+import React from "react"
+
+export const DefaultLayout = props => (
+    <div>
+      <h1>Example showing Medium posts</h1>
+
+      <main>
+        {props.children()}
+      </main>
+    </div>
+  )
+
+export default DefaultLayout
diff --git a/examples/using-medium/src/pages/index.js b/examples/using-medium/src/pages/index.js
new file mode 100644
index 0000000000000..1bf585568f8ec
--- /dev/null
+++ b/examples/using-medium/src/pages/index.js
@@ -0,0 +1,48 @@
+import React from "react"
+
+const mediumCDNUrl = `https://cdn-images-1.medium.com/max/150/`
+
+const IndexPage = ({ data }) => {
+  const posts = data.allMediumPost.edges
+
+  return (
+    <main>
+      {posts.map(post =>
+        <article key={post.node.id}>
+          <h2>
+            {post.node.title}
+          </h2>
+          <h3>
+            by {post.node.author.name}
+          </h3>
+          <img src={`${mediumCDNUrl}/${post.node.virtuals.previewImage.imageId}`} alt={post.node.title} width="150" />
+        </article>
+      )}
+    </main>
+  )
+}
+
+IndexPage.propTypes
+
+export default IndexPage
+
+export const pageQuery = graphql`
+  query IndexQuery {
+    allMediumPost(limit: 5, sort: { fields: [createdAt], order: DESC }) {
+      edges {
+        node {
+          id
+          title
+          author {
+            name
+          }
+          virtuals {
+            previewImage {
+              imageId
+            }
+          }
+        }
+      }
+    }
+  }
+`
diff --git a/packages/gatsby-source-medium/README.md b/packages/gatsby-source-medium/README.md
new file mode 100644
index 0000000000000..4e1274900ec1b
--- /dev/null
+++ b/packages/gatsby-source-medium/README.md
@@ -0,0 +1,59 @@
+# gatsby-source-medium
+
+Source plugin for pulling data into Gatsby from an unofficial Medium JSON
+endpoint. Unfortunately the JSON endpoint does not provide the complete stories,
+but only previews. If you need the complete stories, you might have a look at
+something like [gatsby-source-rss](https://github.com/jondubin/gatsby-source-rss).
+
+## Install
+
+`npm install --save gatsby-source-medium`
+
+## How to use
+
+```javascript
+// In your gatsby-config.js
+plugins: [
+  {
+    resolve: `gatsby-source-medium`,
+    options: {
+      username: `username/publication`
+    }
+  }
+]
+```
+
+## How to query
+
+You can query nodes created from Medium like the following:
+
+```graphql
+
+query StoriesQuery {
+    allMediumPost(sort: { fields: [createdAt], order: DESC }) {
+      edges {
+        node {
+          id
+          title
+          creatorId
+          slug
+          uniqueSlug
+          virtuals {
+            subtitle
+            previewImage {
+              imageId
+            }
+          }
+        }
+      }
+    }
+    allMediumUser {
+      edges {
+        node {
+          id
+          name
+        }
+      }
+    }
+  }
+```
diff --git a/packages/gatsby-source-medium/gatsby-node.js b/packages/gatsby-source-medium/gatsby-node.js
new file mode 100644
index 0000000000000..e475da099bb49
--- /dev/null
+++ b/packages/gatsby-source-medium/gatsby-node.js
@@ -0,0 +1,65 @@
+const axios = require(`axios`)
+const crypto = require(`crypto`)
+
+const fetch = username => {
+  const url = `https://medium.com/${username}/latest?format=json`
+  return axios.get(url)
+}
+
+const prefix = `])}while(1);</x>`
+
+const strip = payload => payload.replace(prefix, ``)
+
+exports.sourceNodes = async ({ boundActionCreators }, { username }) => {
+  const { createNode } = boundActionCreators
+
+  try {
+    const result = await fetch(username)
+    const json = JSON.parse(strip(result.data))
+
+    const { posts } = json.payload
+    const collectionKeys = Object.keys(json.payload.references.Collection)
+    const userKeys = Object.keys(json.payload.references.User)
+
+    const importableResources = [
+      userKeys.map(key => json.payload.references.User[key]),
+      posts,
+      collectionKeys.map(key => json.payload.references.Collection[key]),
+    ]
+
+    const resources = Array.prototype.concat(...importableResources)
+    resources.map(resource => {
+      const digest = crypto.createHash(`md5`).update(JSON.stringify(resource)).digest(`hex`)
+
+      const links =
+        resource.type === `Post`
+          ? {
+              author___NODE: resource.creatorId,
+            }
+          : resource.type === `User`
+            ? {
+                posts___NODE: posts.filter(post => post.creatorId === resource.userId).map(post => post.id),
+              }
+            : {}
+
+      const node = Object.assign(
+        resource,
+        {
+          id: resource.id ? resource.id : resource.userId,
+          parent: `__SOURCE__`,
+          children: [],
+          internal: {
+            type: `Medium${resource.type}`,
+            contentDigest: digest,
+          },
+        },
+        links
+      )
+
+      createNode(node)
+    })
+  } catch (error) {
+    console.error(error)
+    process.exit(1)
+  }
+}
diff --git a/packages/gatsby-source-medium/package.json b/packages/gatsby-source-medium/package.json
new file mode 100644
index 0000000000000..8173f8beddae3
--- /dev/null
+++ b/packages/gatsby-source-medium/package.json
@@ -0,0 +1,10 @@
+{
+  "name": "gatsby-source-medium",
+  "version": "1.0.0",
+  "description": "Gatsby source plugin for building websites using Medium as a data source",
+  "author": "Robert Vogt <robert@smartive.ch>",
+  "license": "MIT",
+  "dependencies": {
+    "axios": "^0.16.2"
+  }
+}