CINXE.COM
Leanpub API Documentation
<!DOCTYPE html> <!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]--> <!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]--> <!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]--> <!--[if gt IE 9]><!--><html class="no-js" lang="en"><!--<![endif]--><head> <meta content='IE=edge' http-equiv='X-UA-Compatible'> <meta charset='utf-8'> <meta content='width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no' name='viewport'> <meta content='en' name='Content-Language'> <link rel="apple-touch-icon-precomposed" type="image/png" href="https://leanpub.com/assets/favicons/apple-touch-icon-57x57-3dc48b9be1873ac9bf6d580e7836e3e5.png" sizes="57x57" /> <link rel="apple-touch-icon-precomposed" type="image/png" href="https://leanpub.com/assets/favicons/apple-touch-icon-60x60-06b95deca3f378372b051ca8ea42cfbd.png" sizes="60x60" /> <link rel="apple-touch-icon-precomposed" type="image/png" href="https://leanpub.com/assets/favicons/apple-touch-icon-72x72-935ba702f9d3da9b4404aa2b797920e5.png" sizes="72x72" /> <link rel="apple-touch-icon-precomposed" type="image/png" href="https://leanpub.com/assets/favicons/apple-touch-icon-76x76-937dbc8b688db389b5b872c5ffdffe2d.png" sizes="76x76" /> <link rel="apple-touch-icon-precomposed" type="image/png" href="https://leanpub.com/assets/favicons/apple-touch-icon-114x114-9db66ed49dfe9c3ed799923955da36e2.png" sizes="114x114" /> <link rel="apple-touch-icon-precomposed" type="image/png" href="https://leanpub.com/assets/favicons/apple-touch-icon-120x120-3fd2359930103db35eb499036b81ba90.png" sizes="120x120" /> <link rel="apple-touch-icon-precomposed" type="image/png" href="https://leanpub.com/assets/favicons/apple-touch-icon-144x144-5bee791d2b53cc426eb14e7f6e40a024.png" sizes="144x144" /> <link rel="apple-touch-icon-precomposed" type="image/png" href="https://leanpub.com/assets/favicons/apple-touch-icon-152x152-af69d0ec0fe11cf82324b06ff9a56e3b.png" sizes="152x152" /> <link rel="icon" type="image/png" href="https://leanpub.com/assets/favicons/favicon-16x16-19545df363d1089bccdc59d17ee5b781.png" sizes="16x16" /> <link rel="icon" type="image/png" href="https://leanpub.com/assets/favicons/favicon-32x32-9a0898109481d6450269c966cdf6a2d7.png" sizes="32x32" /> <link rel="icon" type="image/png" href="https://leanpub.com/assets/favicons/favicon-96x96-98f4372a68f5617fc907b7bde8d94e05.png" sizes="96x96" /> <link rel="icon" type="image/png" href="https://leanpub.com/assets/favicons/favicon-128x128-bf73acc329429fbf555afe3b067aa2d6.png" sizes="128x128" /> <link rel="icon" type="image/png" href="https://leanpub.com/assets/favicons/favicon-196x196-24a71f1b4fb02600f635b59a116daf05.png" sizes="196x196" /> <meta content='Leanpub' name='application-name'> <meta content='#ffffff' name='msapplication-TileColor'> <meta content='favicons/mstile-144x144.png' name='msapplication-TileImage'> <meta content='favicons/mstile-150x150.png' name='msapplication-square150x150logo'> <meta content='favicons/mstile-310x150.png' name='msapplication-wide310x150logo'> <meta content='favicons/mstile-310x310.png' name='msapplication-square310x310logo'> <meta content='favicons/mstile-70x70.png' name='msapplication-square70x70logo'> <title>Leanpub API Documentation</title> <link href='//fonts.googleapis.com/' rel='dns-prefetch'> <script src='https://ajax.googleapis.com/ajax/libs/webfont/1.6.16/webfont.js'></script> <script> if (typeof WebFont !== 'undefined') { WebFont.load({ google: { families: [ 'Noto+Sans:ital,wght@0,400;0,700;1,400;1,700', 'Noto+Serif:ital,wght@0,400;0,700;1,400;1,700', 'Inter:wght@400;700', ] } }); } </script> <link rel="stylesheet" media="screen" href="https://leanpub.com/assets/font_awesome-bbab983f4954a5c7dd952efdd403ebe8.css" /> <link rel="stylesheet" media="all" href="https://leanpub.com/assets/application-48c8d5aa3156e22d7b7c0b588642be6d.css" /> <script> var _rollbarConfig = { accessToken: "3d279f41d3804636adbbba833c2c0d2d", captureUncaught: true, captureUnhandledRejections: true, code_version: "9b7ac20a", payload: { environment: "production" } }; // Rollbar Snippet !function(r){function o(n){if(e[n])return e[n].exports;var t=e[n]={exports:{},id:n,loaded:!1};return r[n].call(t.exports,t,t.exports,o),t.loaded=!0,t.exports}var e={};return o.m=r,o.c=e,o.p="",o(0)}([function(r,o,e){"use strict";var n=e(1),t=e(4);_rollbarConfig=_rollbarConfig||{},_rollbarConfig.rollbarJsUrl=_rollbarConfig.rollbarJsUrl||"https://cdnjs.cloudflare.com/ajax/libs/rollbar.js/2.5.2/rollbar.min.js",_rollbarConfig.async=void 0===_rollbarConfig.async||_rollbarConfig.async;var a=n.setupShim(window,_rollbarConfig),l=t(_rollbarConfig);window.rollbar=n.Rollbar,a.loadFull(window,document,!_rollbarConfig.async,_rollbarConfig,l)},function(r,o,e){"use strict";function n(r){return function(){try{return r.apply(this,arguments)}catch(r){try{console.error("[Rollbar]: Internal error",r)}catch(r){}}}}function t(r,o){this.options=r,this._rollbarOldOnError=null;var e=s++;this.shimId=function(){return e},"undefined"!=typeof window&&window._rollbarShims&&(window._rollbarShims[e]={handler:o,messages:[]})}function a(r,o){if(r){var e=o.globalAlias||"Rollbar";if("object"==typeof r[e])return r[e];r._rollbarShims={},r._rollbarWrappedError=null;var t=new p(o);return n(function(){o.captureUncaught&&(t._rollbarOldOnError=r.onerror,i.captureUncaughtExceptions(r,t,!0),i.wrapGlobals(r,t,!0)),o.captureUnhandledRejections&&i.captureUnhandledRejections(r,t,!0);var n=o.autoInstrument;return o.enabled!==!1&&(void 0===n||n===!0||"object"==typeof n&&n.network)&&r.addEventListener&&(r.addEventListener("load",t.captureLoad.bind(t)),r.addEventListener("DOMContentLoaded",t.captureDomContentLoaded.bind(t))),r[e]=t,t})()}}function l(r){return n(function(){var o=this,e=Array.prototype.slice.call(arguments,0),n={shim:o,method:r,args:e,ts:new Date};window._rollbarShims[this.shimId()].messages.push(n)})}var i=e(2),s=0,d=e(3),c=function(r,o){return new t(r,o)},p=function(r){return new d(c,r)};t.prototype.loadFull=function(r,o,e,t,a){var l=function(){var o;if(void 0===r._rollbarDidLoad){o=new Error("rollbar.js did not load");for(var e,n,t,l,i=0;e=r._rollbarShims[i++];)for(e=e.messages||[];n=e.shift();)for(t=n.args||[],i=0;i<t.length;++i)if(l=t[i],"function"==typeof l){l(o);break}}"function"==typeof a&&a(o)},i=!1,s=o.createElement("script"),d=o.getElementsByTagName("script")[0],c=d.parentNode;s.crossOrigin="",s.src=t.rollbarJsUrl,e||(s.async=!0),s.onload=s.onreadystatechange=n(function(){if(!(i||this.readyState&&"loaded"!==this.readyState&&"complete"!==this.readyState)){s.onload=s.onreadystatechange=null;try{c.removeChild(s)}catch(r){}i=!0,l()}}),c.insertBefore(s,d)},t.prototype.wrap=function(r,o,e){try{var n;if(n="function"==typeof o?o:function(){return o||{}},"function"!=typeof r)return r;if(r._isWrap)return r;if(!r._rollbar_wrapped&&(r._rollbar_wrapped=function(){e&&"function"==typeof e&&e.apply(this,arguments);try{return r.apply(this,arguments)}catch(e){var o=e;throw o&&("string"==typeof o&&(o=new String(o)),o._rollbarContext=n()||{},o._rollbarContext._wrappedSource=r.toString(),window._rollbarWrappedError=o),o}},r._rollbar_wrapped._isWrap=!0,r.hasOwnProperty))for(var t in r)r.hasOwnProperty(t)&&(r._rollbar_wrapped[t]=r[t]);return r._rollbar_wrapped}catch(o){return r}};for(var u="log,debug,info,warn,warning,error,critical,global,configure,handleUncaughtException,handleUnhandledRejection,captureEvent,captureDomContentLoaded,captureLoad".split(","),f=0;f<u.length;++f)t.prototype[u[f]]=l(u[f]);r.exports={setupShim:a,Rollbar:p}},function(r,o){"use strict";function e(r,o,e){if(r){var t;if("function"==typeof o._rollbarOldOnError)t=o._rollbarOldOnError;else if(r.onerror){for(t=r.onerror;t._rollbarOldOnError;)t=t._rollbarOldOnError;o._rollbarOldOnError=t}var a=function(){var e=Array.prototype.slice.call(arguments,0);n(r,o,t,e)};e&&(a._rollbarOldOnError=t),r.onerror=a}}function n(r,o,e,n){r._rollbarWrappedError&&(n[4]||(n[4]=r._rollbarWrappedError),n[5]||(n[5]=r._rollbarWrappedError._rollbarContext),r._rollbarWrappedError=null),o.handleUncaughtException.apply(o,n),e&&e.apply(r,n)}function t(r,o,e){if(r){"function"==typeof r._rollbarURH&&r._rollbarURH.belongsToShim&&r.removeEventListener("unhandledrejection",r._rollbarURH);var n=function(r){var e,n,t;try{e=r.reason}catch(r){e=void 0}try{n=r.promise}catch(r){n="[unhandledrejection] error getting `promise` from event"}try{t=r.detail,!e&&t&&(e=t.reason,n=t.promise)}catch(r){}e||(e="[unhandledrejection] error getting `reason` from event"),o&&o.handleUnhandledRejection&&o.handleUnhandledRejection(e,n)};n.belongsToShim=e,r._rollbarURH=n,r.addEventListener("unhandledrejection",n)}}function a(r,o,e){if(r){var n,t,a="EventTarget,Window,Node,ApplicationCache,AudioTrackList,ChannelMergerNode,CryptoOperation,EventSource,FileReader,HTMLUnknownElement,IDBDatabase,IDBRequest,IDBTransaction,KeyOperation,MediaController,MessagePort,ModalWindow,Notification,SVGElementInstance,Screen,TextTrack,TextTrackCue,TextTrackList,WebSocket,WebSocketWorker,Worker,XMLHttpRequest,XMLHttpRequestEventTarget,XMLHttpRequestUpload".split(",");for(n=0;n<a.length;++n)t=a[n],r[t]&&r[t].prototype&&l(o,r[t].prototype,e)}}function l(r,o,e){if(o.hasOwnProperty&&o.hasOwnProperty("addEventListener")){for(var n=o.addEventListener;n._rollbarOldAdd&&n.belongsToShim;)n=n._rollbarOldAdd;var t=function(o,e,t){n.call(this,o,r.wrap(e),t)};t._rollbarOldAdd=n,t.belongsToShim=e,o.addEventListener=t;for(var a=o.removeEventListener;a._rollbarOldRemove&&a.belongsToShim;)a=a._rollbarOldRemove;var l=function(r,o,e){a.call(this,r,o&&o._rollbar_wrapped||o,e)};l._rollbarOldRemove=a,l.belongsToShim=e,o.removeEventListener=l}}r.exports={captureUncaughtExceptions:e,captureUnhandledRejections:t,wrapGlobals:a}},function(r,o){"use strict";function e(r,o){this.impl=r(o,this),this.options=o,n(e.prototype)}function n(r){for(var o=function(r){return function(){var o=Array.prototype.slice.call(arguments,0);if(this.impl[r])return this.impl[r].apply(this.impl,o)}},e="log,debug,info,warn,warning,error,critical,global,configure,handleUncaughtException,handleUnhandledRejection,_createItem,wrap,loadFull,shimId,captureEvent,captureDomContentLoaded,captureLoad".split(","),n=0;n<e.length;n++)r[e[n]]=o(e[n])}e.prototype._swapAndProcessMessages=function(r,o){this.impl=r(this.options);for(var e,n,t;e=o.shift();)n=e.method,t=e.args,this[n]&&"function"==typeof this[n]&&("captureDomContentLoaded"===n||"captureLoad"===n?this[n].apply(this,[t[0],e.ts]):this[n].apply(this,t));return this},r.exports=e},function(r,o){"use strict";r.exports=function(r){return function(o){if(!o&&!window._rollbarInitialized){r=r||{};for(var e,n,t=r.globalAlias||"Rollbar",a=window.rollbar,l=function(r){return new a(r)},i=0;e=window._rollbarShims[i++];)n||(n=e.handler),e.handler._swapAndProcessMessages(l,e.messages);window[t]=n,window._rollbarInitialized=!0}}}}]); // End Rollbar Snippet </script> <script src="https://www.google.com/recaptcha/api.js?render=6LdDCakUAAAAAEFI0Kyx_gg9t-G4r1mOWrIwFLd0"></script> <script src="https://leanpub.com/assets/modernizr-a38b94cc0625ba4488942166ee4d23a4.js"></script> <script src="//www.google.com/jsapi"></script> <script src="https://leanpub.com/assets/chartkick-ecf78465738cad825935037ff8bba32a.js"></script> <link rel="stylesheet" media="screen" href="https://leanpub.com/assets/stylesheets/header-bundle-373e4821a88cdf3ee5da.css" /> <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'G-XQK3X47PGQ', 'auto'); // Replace with your property ID. ga('require', 'ec'); // Visitor type parameters (only settable from non cached) ga('send', 'pageview'); </script> </head> <body id='help-api'> <div class='flash' id='js-flash-prototype'> <div class='flash__progress-bar'></div> <div class='container--large'> <div class='flash__message'></div> <div class='flash__close-icon'> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="25px" id="Layer_1" style="enable-background:new 0 0 512 512;" version="1.1" viewBox="0 0 512 512" width="25px" xml:space="preserve"><path d="M437.5,386.6L306.9,256l130.6-130.6c14.1-14.1,14.1-36.8,0-50.9c-14.1-14.1-36.8-14.1-50.9,0L256,205.1L125.4,74.5 c-14.1-14.1-36.8-14.1-50.9,0c-14.1,14.1-14.1,36.8,0,50.9L205.1,256L74.5,386.6c-14.1,14.1-14.1,36.8,0,50.9 c14.1,14.1,36.8,14.1,50.9,0L256,306.9l130.6,130.6c14.1,14.1,36.8,14.1,50.9,0C451.5,423.4,451.5,400.6,437.5,386.6z"></path></svg> </div> </div> </div> <script type='text/javascript'> window.__menu_data__ = null window.__menuPath__ = ["help","api"] window.__hidesStoreContent__ = true </script> <div id='react-header-root'></div> <script> window.__BASE_URL__ = "https://leanpub.com/" </script> <script src="https://leanpub.com/assets/polyfill-bundle-f2bf261e3f0b57d19161.js"></script> <script src="https://leanpub.com/assets/header-bundle-373e4821a88cdf3ee5da.js"></script> <div class='scroll-wrapper' id='scroll-wrapper'> <div class='main ruby-main'> <div class='container--medium'> <br> <p> If you can't find what you need here, see the <a href="https://help.leanpub.com/en/articles/3788293-the-leanpub-faq">Leanpub FAQ</a>, the <a href="https://help.leanpub.com/en/articles/4405350-leanpub-author-faq">Leanpub Author FAQ</a>, <a href="https://leanpub.com/manual/read">The Leanpub Manual</a>, <a href="https://community.leanpub.com/c/authors">The Leanpub Authors Forum</a></li>, <a href="https://leanpub.com/markua/read">The Markua Manual</a> or <a href="https://leanpub.com/leanpubflavouredmarkdownmanual/read">The Leanpub Flavoured Markdown Manual</a>. If none of those help, please <a href="https://help.leanpub.com/en/articles/110768-if-i-can-t-find-an-answer-to-my-question-in-this-help-center-what-should-i-do">Contact Us</a>. </p> <hr> <h1 id="the-leanpub-api">The Leanpub API</h1> <h2 id="terms">Terms</h2> <p><strong>Using the Leanpub API requires a Pro plan.</strong> To upgrade to a Pro plan, please go <a href="/membership">here</a>.</p> <p>All use of the Leanpub API must be authenticated with an API key.</p> <p>Access to the Leanpub API may be revoked for any user, at any time, for any reason. In other words, please don't hammer our servers :)</p> <h2 id="authentication">Authentication</h2> <h3 id="getting-your-api-key">Getting your API key</h3> <p>You need to get an API key to start using the API. Go <a href="/user_dashboard/api_key">here</a> to get one. Note that you typically need a Pro plan to get an API key. You can also get an API key if you are the author of a book for an organization which has an Organization Pro Plan, but in that case your API key will only work for that book.</p> <p>Click on the button that says "Enable the Leanpub API" and copy the API key.</p> <h3 id="using-your-api-key">Using your API key</h3> <p>You need to send your API key on every API request you make. For GET requests, append it as a query string with a key of <code>api_key</code>:</p> <pre><code>GET https://leanpub.com/some/path?api_key=YOUR_API_KEY </code></pre> <p>For posts, you can also include the API key as part of the post data with a key of <code>api_key</code>. Here's an example in cURL:</p> <pre><code>curl -d "api_key=YOUR_API_KEY" https://leanpub.com/some/path </code></pre> <h2 id="your-books-slug">Your book's slug</h2> <p>You'll need to know your book's slug. It's the part of the URL for your book after <code>https://leanpub.com/</code>. E.g. if your book is found at <code>https://leanpub.com/your_book</code>, then your book's slug is <code>your_book</code>.</p> <h2 id="previewing-and-publishing">Previewing and Publishing</h2> <h3 id="previewing">Previewing</h3> <p>To start a preview of your book, you POST to <code>https://leanpub.com/SLUG/preview.json</code> passing <code>api_key</code> as part of the post data. Here's an example with cURL:</p> <pre><code>curl -d "api_key=YOUR_API_KEY" https://leanpub.com/SLUG/preview.json </code></pre> <p>To preview a subset of your book using <code>Subset.txt</code>, post to <code>https://leanpub.com/SLUG/preview/subset.json</code>, passing the <code>api_key</code> as usual. A subset preview only generates a PDF, to go as fast as possible. <pre><code>curl -d "api_key=YOUR_API_KEY" https://leanpub.com/SLUG/preview/subset.json</code></pre> <h3 id="previewing-a-single-file">Previewing a single file</h3> <p>Sometimes you just want to take a look at the file you are working on and don't want to mess around with <code>Subset.txt</code> or anything like that. You can do that with the Single File generation API.</p> <p>To use it, make a post to <code>https://leanpub.com/SLUG/preview/single.json</code> with a <code>Content-Type</code> header of <code>text/plain</code> and the body of the post containing the Markdown you want to generate. We will place the resulting PDF in the previews folder of your Dropbox, with a name of <code>SLUG-single-file.pdf</code>.</p> <p>Getting the newlines working when posting with CURL is a bit complicated, so we'll use a Ruby script as an example this time. To use it, set an environment variable called LEANPUB_API_KEY to your Leanpub API key:</p> <pre> export LEANPUB_API_KEY="yourapikey" </pre> <p>Then call the script like this:</p> <pre> ./generate_single_file <book_slug> <path_to_file> </pre> <p>e.g.</p> <pre> ./generate_single_file thes3cookbook /path/to/some/markdown.txt </pre> <p>here's the script:</p> <pre> #!/usr/bin/env ruby require "httpclient" slug = ARGV[0] filename = ARGV[1] api_key = ENV["LEANPUB_API_KEY"] content = File.read(filename) headers = { "Content-Type" => "text/plain"} url = "https://leanpub.com/#{slug}/preview/single.json?api_key=#{api_key}" HTTPClient.new.post(url, :body => content, :header => headers) </pre> <h3 id="publishing">Publishing</h3> <p>To publish your book, you POST to <code>https://leanpub.com/SLUG/publish.json</code> with your api key as part of the post data. This will publish without emailing your readers. Here's an example with cURL:</p> <pre><code>curl -d "api_key=YOUR_API_KEY" https://leanpub.com/SLUG/publish.json </code></pre> <p>If you want to email readers and add some release notes, add keys for <code>publish[email_readers]</code> and <code>publish[release_notes]</code>. The release notes should be URL encoded.</p> <pre><code>curl -d "api_key=YOUR_API_KEY" -d "publish[email_readers]=true" -d "publish[release_notes]=please+let+me+know+what+you+think" https://leanpub.com/SLUG/publish.json </code></pre> <h3 id="getting-the-job-status">Getting the job status</h3> <p>Once you have started a preview or publish, you can get the status of the current job for your book by doing a GET on <code>https://leanpub.com/SLUG/job_status.json</code> with your api key as a query param:</p> <pre><code>curl "https://leanpub.com/SLUG/job_status?api_key=YOUR_API_KEY" </code></pre> <p>The result is a JSON response that looks like this:</p> <pre> { "num": 8, "job_type": "GenerateBookJob#preview", "total": 28, "message": "Downloading organization logo...", "status": "working", "name": "Publish scotttest99", "time": 1376073552, "options": { "requested_by": "peter@leanpub.com", "release_notes": "this is a test\n\nwith two lines", "slug": "scotttest99", "action": "publish", "email_readers": true } } </pre> <p>the <code>num</code> and <code>total</code> keys can be used to say things like "you are on step <code>num</code> of <code>total</code>".</p> <p>You can poll this to see what's happening, but please don't poll more than every five seconds or so. You will get an empty JSON object when the job is complete.</p> <h3 id='getting-the-latest-version-of-your-book'>Getting the Latest Version of your Book</h3> <p>You can get the latest version of your book using the <code>preview_url</code> and <code>published_url</code> returned by the <a href='#getting-book-info'>book summary information API</a>. These are also the URLs on the "Download Latest Preview" and "Download Latest Version" pages for your book (<code>https://leanpub.com/YOUR_BOOK/preview/links</code> and <code>https://leanpub.com/YOUR_BOOK/publish/download_latest_version</code>). These URLs never change, so you only need to get them once per book. They are also something you should keep secret: if someone has these URLs, they can get your book for free.</p> <p>The only trick is that you're going to have to follow a redirect to Amazon S3. If you're using curl, that's as simple as adding the `-L` flag to your curl command:</p> <pre> curl -L https://leanpub.com/s/some-long-uuid.pdf > yourbook.pdf curl -L https://leanpub.com/s/some-long-uuid.epub > yourbook.epub </pre> <h2 id="getting-book-info">Getting Book Summary Information</h2> <p>You can get information about a book using curl with your API key:</p> <pre>curl https://leanpub.com/SLUG.json?api_key=YOUR_API_KEY</pre> <p>Like all Leanpub API calls, this requires authentication. We only show you information about books where you are one of the authors (or if the book is published by an organization, and you are one of the editors).</p> <p>If you are an author of the book, then we will also give you the total copies sold and revenue for the book, as well as the URLs for downloading the current preview and published version of your book in PDF and EPUB formats.</p> <p>The result is a JSON response that looks like this:</p> <pre> { "slug": "thes3cookbook", "subtitle": "Get cooking with Amazon's Simple Storage Service", "title": "The S3 Cookbook", "about_the_book": "Amazon’s Simple Storage Service (S3) has been described as “Storage in the cloud”....", "author_string": "Scott Patten", "url": "https://leanpub.com/thes3cookbook", "title_page_url": "https://s3.amazonaws.com/titlepages.leanpub.com/thes3cookbook/original?1375318991", "minimum_paid_price": "7.99", "suggested_price": "10.0", "page_count_published": 252, "total_copies_sold": 111382, "word_count_published": 43315 } </pre> <p>The <code>page_count_published</code> entry shows the number of pages in the currently published version, and will only be shown if the author has chosen to display the number of pages. Similarly, <code>word_count_published</code> shows the number of words in the currently published version and will only be shown if the author has chosen to display the number of words.</p> <p>Here is what it will look like for an author who has authenticated:</p> <pre> { "slug": "thes3cookbook", "subtitle": "Get cooking with Amazon's Simple Storage Service", "title": "The S3 Cookbook", "about_the_book": "Amazon’s Simple Storage Service (S3) has been described as “Storage in the cloud”....", "last_published_at": "2015-01-15T19:21:50Z", "meta_description": null, "page_count": 263, "page_count_published": 252, "total_copies_sold": 111382, "total_revenue": "1123334.14", "word_count": 43315, "word_count_published": 43315, "author_string": "Scott Patten", "url": "https://leanpub.com/thes3cookbook", "title_page_url": "https://s3.amazonaws.com/titlepages.leanpub.com/thes3cookbook/original?1435903728", "minimum_paid_price": "7.99", "suggested_price": "10.0", "image": "https://s3.amazonaws.com/titlepages.leanpub.com/thes3cookbook/medium?1435903728", "possible_reader_count": 0, "pdf_preview_url": "https://leanpub.com/s/redacted-string.pdf", "epub_preview_url": "https://leanpub.com/s/redacted-string.epub", "pdf_published_url": "https://leanpub.com/s/another-redacted-string.pdf", "epub_published_url": "https://leanpub.com/s/another-redacted-string.epub" } </pre> <p><code>page_count_published</code> is the page count of the latest published version. <code>page_count</code> is the page count of the latest previewed or published version. <code>word_count_published</code> and <code>word_count</code> are the same, but for word count (and yes, those revenue numbers are totally made up).</p> <h2 id="getting-sales-data">Getting sales data</h2> <h3>Summary Sales Data</h3> <p>You can get a summary of your sales and royalties at</p> <pre>https://leanpub.com/SLUG/royalties.json</pre> <p>or</p> <pre>https://leanpub.com/SLUG/royalties.xml</pre> <p>Here's an example with curl:</p> <pre><code>curl "https://leanpub.com/SLUG/royalties.json?api_key=YOUR_API_KEY"</code></pre> <h3>All Sales Data</h3> <p>You can access data for all of your individualy purchases in JSON and XML format by doing a GET request to<br /> <pre>https://leanpub.com/SLUG/individual_purchases.json</pre> <p>or</p> <pre>https://leanpub.com/SLUG/individual_purchases.xml</pre> <p>This data is paginated, and by default gives you your last 50 sales. Here's an example to get the last 50 sales of your book:</p> <pre><code>curl "https://leanpub.com/SLUG/individual_purchases.json?api_key=YOUR_API_KEY" </code></pre> <p>If you want to get the next 50 sales, request page 2 of your sales data like this:</p> <pre><code>curl "https://leanpub.com/SLUG/individual_purchases.json?api_key=YOUR_API_KEY&page=2" </code></pre> <h2 id="coupons">Coupons</h2> <p>You can get information about the coupons for a given book and create new coupons for a book.</p> <h3>Getting a list of coupons for a book</h3> <p>To get a list of coupons, make a GET request to</p> <pre>https://leanpub.com/SLUG/coupons.json</pre> <p>or</p> <pre>https://leanpub.com/SLUG/coupons.xml</pre> <p>The response will include a list of all coupons for your book. Here is an example JSON response for a book with a single coupon:</p> <pre> [ { "coupon_code": "NOT_A_REAL_COUPON", "created_at": "2013-04-17T22:12:58Z", "package_discounts": [ { "package_slug": "book", "discounted_price": 2.0 }, { "package_slug": "teamedition", "discounted_price": 4.0 } ], "end_date": "2016-05-17", "max_uses": null, "note": "This is not a real coupon", "num_uses": 12, "start_date": "2013-04-17", "suspended": false, "updated_at": "2013-04-17T22:12:58Z", "book_slug": "yourbook" } ] </pre> <h3>Creating coupons</h3> <p>You can create coupons by POSTing to <code>https://leanpub.com/SLUG/coupons.json</code>.</p> <p>You need to send the following data:</p> <table> <thead> <tr> <th>Name</th> <th>Required?</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>coupon_code</code></td> <td>Yes</td> <td>The coupon code for this coupon. This must be unique for your book.</td> </tr> <tr> <td><code>package_discounts_attributes</code></td> <td>Yes</td> <td>An array of discounts, one for each package that you want this coupon to apply to. Each discount is an object with two keys: <code>package_slug</code> is the slug of the package that this discount applies to, and <code>discounted_price</code> is the amount that the package can be purchased for.</td> </tr> <tr> <td><code>start_date</code></td> <td>Yes</td> <td>The date the coupon is valid from. Formatted like YYYY-MM-DD.</td> </tr> <tr> <td><code>end_date</code></td> <td>No</td> <td>The date the coupon is valid until. Formatted like YYYY-MM-DD.</td> </tr> <tr> <td><code>max_uses</code></td> <td>No</td> <td>The max number of uses available for a coupon. An integer. Null means unlimited.</td> </tr> <tr> <td><code>note</code></td> <td>No</td> <td>A description of the coupon. This is just used to remind you of what it was for.</td> </tr> <tr> <td><code>suspended</code></td> <td>No</td> <td>Whether or not the coupon is suspended. <code>true</code> or <code>false</code>.</td> </tr> </tbody> </table> <br /> <p>You can send data as either form-encoded data or JSON. If you send JSON, you must set the <code>Content-Type</code> header to <code>application/json</code>.</p> <p>Here is an example using curl with json data:</p> <pre> curl -H "Content-Type: application/json" -X POST \ -d '{"coupon": {"coupon_code":"coupon-code-123456","package_discounts_attributes":[{"package_slug":"book","discounted_price":0.0}], "start_date":"2013-12-28"}}' \ "https://leanpub.com/thes3cookbook/coupons.json?api_key=YOUR_API_KEY" </pre> <p>If you use form-encoded data, then all of the fields must be prefixed with <code>coupon</code>. I.e., instead of sending up <code>coupon_code</code>, you send up <code>coupon[coupon_code]</code>. Here's an example:</pre> <pre> curl -d "coupon[coupon_code]=coupon-code-123456" -d "coupon[discounted_price]=0.0" \ -d "coupon[start_date]=2013-10-21" -d "api_key=YOUR_API_KEY" \ -d "coupon[package_discounts_attributes][][package_slug]=book" -d "coupon[package_discounts_attributes][][discounted_price]=0.00" https://leanpub.com/YOUR_BOOK/coupons.json </pre> <h3>Updating Coupons</h3> <p>You can update a coupon by making a PUT request to <code>https://leanpub.com/SLUG/coupons/COUPON_CODE.json</code>. You use all of the same parameters as when you create a coupon, but none are required. Here is an example using curl:</p> <pre> curl -H "Content-Type: application/json" -XPUT -d '{"suspended":false}' \ "https://leanpub.com/SLUG/coupons/some_coupon_code.json?api_key=YOUR_API_KEY" </pre> <h3>Getting a List of Your Readers' Emails</h3> <p>You can get a list of the email addresses of those readers who have decided to share their email with you by making a GET request to <code>https://leanpub.com/u/USERNAME/reader_emails.json?api_key=YOUR_API_KEY</code>. This returns a JSON formatted array of user emails.</p> <p>These are the parameters:</p> <table> <thead> <tr> <th>Name</th> <th>Required?</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>api_key</code></td> <td>Yes</td> <td>Your api key. This must be the api key for the user specified by USERNAME in the url.</td> </tr> <tr> <td><code>purchase_type</code></td> <td>No</td> <td>The accepted values for this are <code>book</code>, <code>course</code> or <code>all</code> returning the list for your books, courses or both respectively. If no <code>purchase_type</code> is provided, the default is <code>all</code>.</td> </tr> </tbody> </table> <p>For example a query would look like</p> <pre> curl "https://leanpub.com/u/USERNAME/reader_emails.json?api_key=API_KEY&purchase_type=book" </pre> <p>This would return like</p> <pre> [ "someuser@gmail.com", "other.user@outlook.com" ] </pre> <p>Where "someuser@gmail.com" and "other.user@outlook.com" are emails of readers who have shared their email with you.</p> <h2 id="previewing-and-publishing-course">Previewing and Publishing Your Course</h2> <h2 id="your-courses-slug">Your course's slug</h2> <p>If you have self published your course you'll need to know your course's slug. It's the part of the URL for your course after <code>https://leanpub.com/c/</code>. E.g. if your course is found at <code>https://leanpub.com/c/your_course</code>, then your book's slug is <code>your_course</code>.</p> <p>If your course is published under an organization on Leanpub then you will need both the course and organization slug. If your course is published under an organization it's landing page URL will look like <code>https://leanpub.com/courses/ORGANIZATION/SLUG</code> so if your course is found at <code>https://leanpub.com/courses/your_organization/your_course</code> the organization slug would be <code>your_organization</code> and the course slug would be <code>your_course</code>.</p> <p>If your course is published under a university on Leanpub then you will need both the course and university slug. If your course is published under a university it's landing page URL will look like <code>https://leanpub.com/universities/courses/UNIVERSITY/COURSE</code> so if your course is found at <code>https://leanpub.com/universities/courses/your_university/your_course</code> the university slug would be <code>your_university</code> and the course slug would be <code>your_course</code>.</p> <h3 id="previewing-course">Previewing</h3> <p>To start a preview of your course, you POST to <code>https://leanpub.com/c/SLUG/preview.json</code> if your course is self-published or <code>https://leanpub.com/c/ORGANIZATION/SLUG/preview.json</code> if your course is under an organization replacing <code>ORGANIZATION</code> slug with <code>UNIVERSITY</code> slug if your course is under a university and passing <code>api_key</code> as part of the post data. Here's some examples with cURL:</p> <pre><code>curl -d "api_key=YOUR_API_KEY" https://leanpub.com/c/SLUG/preview.json </code></pre> <pre><code>curl -d "api_key=YOUR_API_KEY" https://leanpub.com/c/ORGANIZATION/SLUG/preview.json </code></pre> <pre><code>curl -d "api_key=YOUR_API_KEY" https://leanpub.com/c/UNIVERSITY/SLUG/preview.json </code></pre> <h3 id="publishing-course">Publishing</h3> <p>To publish your course, you POST to <code>https://leanpub.com/c/SLUG/publish.json</code> if your course is self-published or <code>https://leanpub.com/c/ORGANIZATION/SLUG/publish.json</code> if your course is under an organization replacing <code>ORGANIZATION</code> slug with <code>UNIVERSITY</code> slug if your course is under a university and passing <code>api_key</code> as part of the post data. Here's some examples with cURL:</p> <pre><code>curl -d "api_key=YOUR_API_KEY" https://leanpub.com/c/SLUG/publish.json </code></pre> <pre><code>curl -d "api_key=YOUR_API_KEY" https://leanpub.com/c/ORGANIZATION/SLUG/publish.json </code></pre> <pre><code>curl -d "api_key=YOUR_API_KEY" https://leanpub.com/c/UNIVERSITY/SLUG/publish.json </code></pre> </div> </div> <footer class='footer'> <div class='container--small'> <h1 class='footer-logo'> <a href="https://leanpub.com/"><img src="https://leanpub.com/assets/logos/logo-white-96-67-2x-cbf54f68046da93161c6375a4d74259d.png" alt="Logo white 96 67 2x" /> </a></h1> <h3 class='footer-slogan'>Publish Early, Publish Often</h3> <ul class='footer-list-break'> <li> <h5 class='footer-headings'>Path</h5> </li> <li class='footer-descriptions-top'>There are many paths, but the one you're on right now on Leanpub is:</li> <li class='path'> Help › Api </li> </ul> <div class='footer-links'> <ul class='footer-list'> <ul class='footer-columns'> <li> <h5 class='footer-headings'>READERS</h5> </li> </ul> <ul class='footer-list-break'> <li> <h5 class='footer-list-title'>Newsletters</h5> </li> <li><a href="/newsletters">Weekly Sale</a></li> <li><a href="/newsletters">Monthly Sale</a></li> </ul> <ul class='footer-list-break'> <li> <h5 class='footer-list-title'>Store</h5> </li> <li><a href="/">Home</a></li> <li><a href="/redeem">Redeem a Token</a></li> <li><a href="/bookstore/book?search">Search</a></li> </ul> <ul class='footer-list-break'> <li> <h5 class='footer-list-title'>Support</h5> </li> <li><a href="https://help.leanpub.com/en/articles/3788293-the-leanpub-faq">Leanpub FAQ</a></li> <li><a href="https://help.leanpub.com/en/articles/4405350-leanpub-author-faq">Leanpub Author FAQ</a></li> <li><a href="https://help.leanpub.com/en/">Search our Help Center</a></li> <li><a href="https://help.leanpub.com/en/articles/110768-if-i-can-t-find-an-answer-to-my-question-in-this-help-center-what-should-i-do">How to Contact Us</a></li> </ul> <ul class='footer-list-break'> <li> <h5 class='footer-headings'>FRONTMATTER PODCAST</h5> </li> <li><a href="/podcasts/featured">Featured Episode</a></li> <li><a href="/podcasts/frontmatter">Episode List</a></li> </ul> <ul class='footer-list-break'> <li> <h5 class='footer-headings'>MEMBERSHIPS</h5> </li> <li><a href="/reader_memberships">Reader Memberships</a></li> <li><a href="/departmentmemberships">Department Reader Memberships</a></li> <li><a href="/author_memberships">Author Memberships</a></li> <li><a href="/membership">Your Membership</a></li> </ul> <ul class='footer-list-break'> <li> <h5 class='footer-headings'>COMPANY</h5> </li> <li> <h5 class='footer-list-title'>About</h5> </li> <li><a href="/about">About Leanpub</a></li> <li><a href="/blog">Blog</a></li> <li><a href="/contact">Contact</a></li> <li><a href="/press">Press</a></li> </ul> <ul class='footer-list-break'> <li> <h5 class='footer-list-title'>Essays</h5> </li> <li><a href="/ai_services">AI Services</a></li> <li><a href="/imagine_a_world">Imagine a world...</a></li> <li><a href="/manifesto">Manifesto</a></li> </ul> <ul class='footer-list-break'> <li> <h5 class='footer-list-title'>More</h5> </li> <li><a href="/partner_program">Partner Program</a></li> <li><a href="/causes">Causes</a></li> <li><a href="/accessibility">Accessibility</a></li> </ul> </ul> <ul class='footer-list'> <ul class='footer-columns'> <li> <h5 class='footer-headings'>AUTHORS</h5> </li> </ul> <ul class='footer-list-break'> <li> <h5 class='footer-list-title'>Write and Publish on Leanpub</h5> </li> <li><a href="/create/book">Create a Book</a></li> <li><a href="/author_dashboard/new_bundle">Create a Bundle</a></li> <li><a href="/create/course">Create a Course</a></li> <li><a href="/course_set_admin/leanpub/course_sets/new">Create a Track</a></li> <li><a href="/testimonials">Testimonials</a></li> <li><a href="/authors">Why Leanpub</a></li> </ul> <ul class='footer-list-break'> <li> <h5 class='footer-list-title'>Services</h5> </li> <li><a href="/course_ai/buy">CourseAI</a></li> <li><a href="/translate_ai/buy">TranslateAI</a></li> </ul> <ul class='footer-list-break'> <li> <h5 class='footer-list-title'>Author Newsletter</h5> </li> <li><a href="/newsletters">The Leanpub Author Update</a></li> </ul> <ul class='footer-list-break'> <li> <h5 class='footer-list-title'>Author Support</h5> </li> <li><a href="https://help.leanpub.com/author-help">Author Help Center</a></li> <li><a href="https://community.leanpub.com/c/authors">Leanpub Authors Forum</a></li> <li><a href="https://leanpub.com/manual/read">The Leanpub Manual</a></li> <li><a href="/help/supported_languages">Supported Languages</a></li> <li><a href="https://leanpub.com/lfm/read">The LFM Manual</a></li> <li><a href="https://leanpub.com/markua/read">Markua Manual</a></li> <li><a href="/help/api">API Docs</a></li> </ul> <ul class='footer-list-break'> <li> <h5 class='footer-list-title'>Organizations</h5> </li> <li><a href="/organizations">Learn More</a></li> <li><a href="/p/register">Sign Up</a></li> </ul> <ul class='footer-list-break'> <li> <h5 class='footer-headings'>LEGAL</h5> </li> <li><a href="/terms">Terms of Service</a></li> <li><a href="/takedown">Copyright Policy</a></li> <li><a href="/privacy">Privacy Policy</a></li> <li><a href="/refunds">Refund Policy</a></li> </ul> </ul> </div> <p class='footer-copyright'> * * * </p> <p class='footer-copyright'> Leanpub is copyright © 2010-2024 <a href="http://ruboss.com">Ruboss Technology Corp.</a><br/> All rights reserved. </p> <p class='footer-copyright'> This site is protected by reCAPTCHA<br/> and the Google <a rel='noopener noreferrer' target='_blank' href="https://policies.google.com/privacy">Privacy Policy</a> and <a rel='noopener noreferrer' target='_blank' href="https://policies.google.com/terms">Terms of Service</a> apply. </p> </div> </footer> <div id='react-modal'></div> </div> <div class='cookies-banner alert alert--info' style='display: none'> Leanpub requires cookies in order to provide you the best experience. <a class='dismiss link'>Dismiss</a> </div> <script type='text/javascript'> window.addEventListener('load', function() { var shouldShowCookies = document.cookie.indexOf('should_show_cookies') !== -1 if (shouldShowCookies) { var banner = document.querySelector('.cookies-banner') // IE < 9 check if (banner.style.removeProperty) { banner.style.removeProperty('display'); } else { banner.style.removeAttribute('display'); } document.querySelector('.cookies-banner').classList.add('shown') // Note that we have to use vanilla JS here because ujs (remote links) code doesn't live in the react app, and i don't // want to have to write this shit twice. document.querySelector('.cookies-banner .dismiss').addEventListener('click', function() { document.querySelector('.cookies-banner').remove() var xhr = new XMLHttpRequest() xhr.open("POST", "/api/v1/accepted_terms/dismiss_cookies", true); xhr.send() }) } }) </script> <script src="https://leanpub.com/assets/application-88e446d6bb15c5682db3fb765baa70b6.js"></script> <!-- Twitter universal website tag code --> <script> !function(e,t,n,s,u,a){e.twq||(s=e.twq=function(){s.exe?s.exe.apply(s,arguments):s.queue.push(arguments); },s.version='1.1',s.queue=[],u=t.createElement(n),u.async=!0,u.src='//static.ads-twitter.com/uwt.js', a=t.getElementsByTagName(n)[0],a.parentNode.insertBefore(u,a))}(window,document,'script'); // Insert Twitter Pixel ID and Standard Event data below twq('init','nw0pa'); twq('track','PageView'); </script> <!-- End Twitter universal website tag code --> </body></html>