PhantomJsCloud Automation API
    Preparing search index...

    PhantomJsCloud Automation API

    Automation API Documentation

    Mouse Clicks, Typing, etc...

    NEW!

    This Automation API was released in Aug 2019. While it is highly usable, certain things may change slightly over the next few months. We will do our best to ensure backwards compatibility, and all modifications will be listed here:

    • 2026-11 Puppeteer commands: element and JavaScript handles, frames, locators, exposed functions, predicate request/response waits and more page and mouse commands (see below). Existing commands are unchanged.
    • 2020-07-24 Add page.emulateTimezone() and update to latest Chrome version. No breaking changes.
    • 2019-08-01 Initial release. Bug fixes, no breaking changes.
    • 2019-07-25 Preview release.

    Click here to go back to the Standard JSON API Docs

    This API is under active development. Please email Support@PhantomJsCloud.com and we will reply back ASAP, hopefully with a solution to your problem.

    Why use it

    This api allows unparalleled control over your browser request. Using this Automation API you will be able to do the following things easily:

    • USER INPUT: Control the Keyboard, Mouse, or Touchscreen directly, as a human would.
    • MULTI-RENDERS: Render multiple screenshots or PDF's of the page, at times you decide.
    • CUSTOM LOGIC: A secure sandbox allows custom code (ES2018 javascript) execution inside your API call. This enables dynamic query/manipulation of the page. Full control over the browser.
    • PUPPETEER SCRIPTS: This API implements most of the Puppeteer page API, allowing for Puppeteer script compatibility: element and JavaScript handles, frames, locators and exposed functions included (see Puppeteer commands). Current limitations are:
      • Limited to a single page object. You can not create more page objects.
      • Page.on() events are limited to callbacks. No callback sub-function calls (like request.abort()) are implemented.
      • The other features listed under Not available.

    Prerequisites

    • Basic Javascript knowledge is recommended. It's best to be familiar with modern Javascript features. It's extremely important to understand the await keyword.
    • DOM Selectors: The easiest way to manipulate the page is to query it using CSS Selectors. Please familiarize yourself with them. An easy way to play with them is:
      • Open Chrome
      • Navigate to a target page (goto http://www.example.com)
      • Open DevTools (Press F12)
      • Open Console (Press Esc)
      • Experiment with Selectors (Type $("h1"); )
    • Use our POST API: While it's possible to call the Automation API via GET it is extremely cumbersome due to the need to urlEncode all non-alphanumeric characters. You really should should construct a JSON object and submit that to our API via the POST body.

    Usage

    The main PhantomJsCloud API is used by adding properties to your request's JSON (A Declarative API). This new Automation API is driven by a procedural "overseer" script. This script is executed in a special sandbox that has access to the API documented by this page.

    To use this new Automation API, embed your script into the new pageRequest.overseerScript property of the Main Declarative API's REQUEST-JSON

    Here is an example of what your REQUEST-JSON would look like:

    //a IPageRequest you can submit to the PhantomJsCloud POST API.
    {
    //navigate to our automation test page
    url:"http:/PhantomJsCloud.com/examples/corpus/automation.html",
    //outputs a jpeg. Note the value of the Label in the screenshot
    renderType:"jpeg",
    //request will pause until the DOMReady event, then click the button which changes the label
    overseerScript:'await page.waitForNavigation({waitUntil:"domcontentloaded"}); page.click("input#btn_id_1");',
    }

    You can try the above example by clicking here.

    The new overseerScript offers full control over what page(s) are loaded, what to do on the page, and what results are stored.

    To go with this flexibility, we added a new renderType:"automation". This will output a JSON containing only the results associated with your overseerScript.

    If you do not choose to use the new "automation" renderType, your automation results are still available if you choose outputAsJson:true as described here. Automation results will be found under userResponse.pageResponses.automation.

    Here is an example showing some of the capabilities of the new "automation" renderType:

    //a IPageRequest you can submit to the PhantomJsCloud POST API.
    {
    //navigate to our automation test page
    url:"http:/PhantomJsCloud.com/examples/corpus/automation.html",
    //outputs automation results
    renderType:"automation",
    //request will pause until the DOMReady event, log the value of the label, click the button which changes the label, then log the label value again.
    //then values are stored for the other automation JSON outputs: rendering, storage, and errorLogs.
    overseerScript:'await page.waitForNavigation({waitUntil:"domcontentloaded"}); \
    await page.meta.log(await page.content({selector:"label#lbl_id_1",type:"plainText"})); \
    await page.click("input#btn_id_1"); \
    await page.meta.log(await page.content({selector:"label#lbl_id_1",type:"plainText"})); \
    //VERY IMPORTANT: comments need a newline character at the end, otherwise the following lines do not get executed. \n \
    page.render.content(); \
    page.meta.store.set("key1",{some:"json"}); \
    page.meta.logError("test error",{no:1});',
    }

    Examples

    This example shows how to render both a screenshot and page HTML in a single request. You can try this example by clicking here.

    // JSON you can submit to the PhantomJsCloud POST API.
    {
    // navigate to example.com
    url:"http://www.example.com",
    // outputs JSON containing only automation results
    renderType:"automation",
    // request will pause until the page LOAD event, then render HTML and a JPEG.
    overseerScript:'await page.waitForNavigation(); \n page.render.content(); \n await page.render.screenshot();',
    // please note that the above overseerScript is a single string containing javascript.
    }

    Please note the use of await in front of page.render.screenshot(). This causes the overseerScript to pause until the screenshot is taken.

    As the above example, this and all overseerScript examples follow a similar workflow, quite easy once you get used to it.

    ///a IPageRequest you can submit to the PhantomJsCloud POST API.
    {
    //no url is sent. instead, the overseerScript decides what URL and navigates to it.
    //url:""
    //after the overseerScript finishes executing, a JPEG will be rendered
    renderType:"jpeg",
    //request will pause until the DOMReady event, then render HTML and a JPEG.
    overseerScript:' \n\
    await page.goto("http:/PhantomJsCloud.com/examples/corpus/automation.html"); //nav to url, plus wait for it to complete \n\
    await page.click("input[name=\'txt_1\']"); //click on the input box and wait till finished. \n\
    await page.keyboard.press("Backspace",{times:5}); //delete 5 characters and wait till finished. \n\
    await page.keyboard.type("TYPED!",{delay:10}); //type some text and wait till finish \n\
    await page.waitForDelay(2000); //wait 2 seconds \n\
    ',
    //please note that in the above overseerScript lines we add linebreak characters and then a "\" character before a linebreak.
    //If this is difficult to understand, just don't use linebreaks, and don't use comments in your script and you should be fine.
    }

    This example injects a custom script and then instructs it to highlight the word "Example". Please take note that to execute javascript in the target page, we use the page.evaluate() function.
    You can try this example by clicking here.

    {
    "renderType": "jpeg",
    "overseerScript":'await page.goto("http://phantomjscloud.com/examples/corpus/example.com.html"); await page.addScriptTag({url:"http://PhantomJsCloud.com/examples/scripts/hilitor.js"}); await page.evaluate(()=>{let _hilitor=new Hilitor(); _hilitor.apply("Example");});',
    }

    Here are three ways to login and take a screenshot.

    (For an example of logging into LinkedIn, check the Advanced Scenario Samples)

    PLEASE NOTE: In this first example, when clicking the Login button we don't await, instead immediately move to the next line which waits for a page navigation.

    ///a IPageRequest you can submit to the PhantomJsCloud POST API.
    {
    //Normal API navigates to this target page
    url:"http://PhantomJsCloud.com/examples/corpus/automation.html",
    //after the overseerScript finishes executing, a JPEG will be rendered
    renderType:"jpeg",
    //will wait until the text input html element is available, then logs in, waiting for the following page to domReady before finishing.
    overseerScript:' await page.waitForSelector("input[name=id]"); \
    await page.type("input[name=id]","user",{delay:100}); \
    await page.type("input[name=pass]","pass",{delay:100}); \
    page.click("input[type=button][value=Login]"); \
    await page.waitForNavigation(); ',
    //please note that in the above overseerScript lines we add linebreak characters and then a "\" character before a linebreak.
    //If this is difficult to understand, just don't use linebreaks, and don't use comments in your script and you should be fine.
    }

    Here's the same workflow as the above example but doing everything via the overseerScript (output is JSON)

    ///a IPageRequest you can submit to the PhantomJsCloud POST API.
    {
    //render a json of just the overseerScript output
    renderType:"automation",
    //will load the url, wait until the text input html element is available, then logs in, waiting for the following page to domReady before taking a screenshot.
    overseerScript:' await page.goto("http://PhantomJsCloud.com/examples/corpus/automation.html"); \
    await page.waitForSelector("input[name=id]"); \
    await page.type("input[name=id]","user",{delay:100}); \
    await page.type("input[name=pass]","pass",{delay:100}); \
    page.click("input[type=button][value=Login]"); \
    await page.waitForNavigation(); \
    await page.render.content(); \
    await page.render.screenshot();',
    //please note that in the above overseerScript lines we add linebreak characters and then a "\" character before a linebreak.
    //If this is difficult to understand, just don't use linebreaks, and don't use comments in your script and you should be fine.
    }

    And here we use Promise Chaining along with page.done() instead of pausing via await

    ///a IPageRequest you can submit to the PhantomJsCloud POST API.
    {
    //render a json of just the overseerScript output
    renderType:"automation",
    //will load the url, wait until the text input html element is available,
    //then logs in, waiting for the following page to domReady before taking a screenshot.
    overseerScript:' page.goto("http://PhantomJsCloud.com/examples/corpus/automation.html"); \
    await page.waitForSelector("input[name=id]"); \
    page.manualWait(); \
    return page.type("input[name=id]","user",{delay:100}) \
    .then(()=>{ return page.type("input[name=pass]","pass",{delay:100}); }) \
    .then(async ()=>{ \
    page.click("input[type=button][value=Login]"); \
    await page.waitForNavigation(); \
    await page.render.content(); \
    await page.render.screenshot(); \
    page.done(); \
    }); ',
    }

    This example renders a jpeg as soon as the domReady event is triggered, resulting in a more than 2x speedup of the cnn.com website. Rendering at DomReady is much faster but depending on the site may be less accurate. You can try this example by clicking here.

    {
    "url":"http://cnn.com",
    "renderType": "jpeg",
    "overseerScript":'await page.waitForNavigation({waitUntil:"domcontentloaded"}); page.done();',
    }

    For more examples, check the Advanced Scenario Samples

    Puppeteer commands

    Besides the commands above, your overseerScript can use these Puppeteer commands. They work like their Puppeteer counterparts, with the differences listed here and on each command's page.

    page.$(), page.$$() and page.evaluateHandle() return live references to objects in the page: ElementHandle:class for DOM elements, JSHandle:class for other values. Call their methods (click, type, boundingBox, screenshot, $eval, ...) or pass them as arguments to page.evaluate().

    const items = await page.$$("ul.results li");
    for (const item of items) {
    await page.meta.log(await item.evaluate((el) => el.textContent));
    }
    • Handles end with the render. A render holds at most 5,000 live handles: release the ones you no longer need with handle.dispose().
    • An element screenshot() returns base64 text; its path option is ignored.

    page.frames() returns every frame of the page as a Frame:class object, the main frame first; page.waitForFrame() waits for one. A Frame has the page-style commands (goto, evaluate, $, click, waitForSelector, locator, ...) for that frame.

    const frame = await page.waitForFrame((f) => f.url().includes("/embedded/"));
    await frame.click("button.play");
    • A Frame object is valid within one request.
    • page.mainFrame() returns a frame summary, a plain object ({url, name, guid}), as before; use page.frames()[0] for the main frame's Frame object. A page.on() frame event handler gets an event object whose fields are methods (frame.url(), frame.name(), frame.guid()); frame.toJson() returns the plain {url, name, guid} summary.
    • addScriptTag() / addStyleTag() take url or content; path is refused.

    page.locator() describes an element and acts on it once it is ready (attached, visible, enabled, stable), retrying until its timeout. See Locator:class.

    await page.locator("#email").fill("me@example.com");
    await page.locator("button[type=submit]").click();
    await Locator.race([page.locator(".accept"), page.locator(".agree")]).click();
    • A locator can chain at most 64 options; Locator.race nests at most 4 deep and holds at most 64 locators.

    page.exposeFunction(name, fn) adds window[name] to the page; the page calls it and your function runs in your overseerScript.

    await page.exposeFunction("report", (text) => page.meta.log("page says", text));
    await page.evaluate(() => window.report(document.title));
    • Arguments and results are JSON values. A DOM element argument arrives as an element handle valid for that call only; a handle cannot be returned.
    • Exposed functions are removed when the render ends.

    page.waitForRequest() and page.waitForResponse() also take a predicate function. The predicate receives each request or response as an object with methods ( HTTPRequest:class: url(), method(), headers(), ...; HTTPResponse:class: url(), status(), headers(), ...). The wait still returns the plain summary object.

    const res = await page.waitForResponse((r) => r.url().includes("/api/") && r.status() === 200);
    await page.meta.log(res.url, res.status);
    • Page: waitForNetworkIdle, getDefaultTimeout, getDefaultNavigationTimeout, isClosed, isJavaScriptEnabled, setJavaScriptEnabled, setBypassCSP, setCacheEnabled, emulateNetworkConditions, emulateIdleState, emulateVisionDeficiency, emulateFocusedPage, emulateCPUThrottling, removeScriptToEvaluateOnNewDocument, setBypassServiceWorker, isServiceWorkerBypassed, setDragInterception, isDragInterceptionEnabled, and page.coverage ( Coverage).
    • Mouse: wheel, reset, drag, dragEnter, dragOver, drop, dragAndDrop. Drag data that names files is refused.

    Commands on the main frame wait until the page has finished loading the request's url (when you set one), so they act on that page instead of the blank start page. The waits waitForNavigation, waitForSelector, waitForXPath, waitForFunction, waitForRequest, waitForResponse, waitForFrame and a locator's wait() / waitHandle() do not wait for it: they start at once, so they can observe that navigation.

    page.touchscreen.touchStart(x, y) starts a touch and returns a TouchHandle:class with move(x, y) and end(); page.touchscreen.touchMove(x, y) and touchEnd() act on the first active touch. Element handles have touchStart(), touchMove(touch?) and touchEnd(). page.touchscreen.tap(x, y) works as before.

    const touch = await page.touchscreen.touchStart(100, 200);
    await touch.move(100, 50);
    await touch.end();
    • At most 64 touches can be active at once. Every touch still active ends when the render ends.
    • A move or end with no active touch, or on a touch that has already ended, is rejected with a message saying what to do.

    page.tracing.start(options) records a Chrome performance trace and page.tracing.stop() returns it as base64 text (decode it to get the trace JSON, which Chrome DevTools opens).

    await page.tracing.start({ screenshots: true });
    await page.goto("https://example.com");
    const trace = await page.tracing.stop(); // base64 text
    page.meta.store.set("trace", trace);
    • Size warning: keep traces to 5 seconds or less. A trace larger than 20 MB is refused: stop() fails and returns no trace. A busy page can record about 15 MB in 5 seconds (less while it sits behind pjsc's automation page); the base64 text is a third larger than the trace (up to about 27 MB).
    • Automatic stop: the recording stops by itself 5 seconds after start() when stop() was not called yet. stop() still returns that trace later in the same render; a trace never read is dropped when the render ends.
    • Chrome's trace buffer is bounded, so on a very busy page the recording can end before 5 seconds; the trace stays valid.
    • One trace at a time: start() fails while a trace is recording or waiting for its stop(), and stop() with no trace fails. Several stop() calls at the same time all get the same trace.
    • The trace categories are fixed (Puppeteer's default set; screenshots: true adds screenshots), and a categories option is refused. path is ignored: no file is written on the server.
    • The trace records the whole browser, so it can also show pjsc's own automation page beside yours.

    These long-standing commands keep their behavior; where it differs from Puppeteer:

    • Frames: without selectFrame, commands such as click, type, evaluate, $eval, content and waitForSelector act on every frame of the page (Puppeteer: the main frame only). Pass { selectFrame: "main" } or use page.frames()[0] for the main frame.
    • Typing: page.type() waits 50 ms between keys unless you pass delay (Puppeteer: 0).
    • Navigation waits: goBack(), goForward(), reload() and setContent() do not wait for the load event unless you pass waitUntil (Puppeteer: "load").
    • Navigation results: goto(), goBack(), goForward() and reload() do not return Puppeteer's response object, and waitForNavigation() returns a frame summary; use page.waitForResponse() or a frame's goto() / waitForNavigation() (page.frames()[0]) for one.
    • Return values: addScriptTag() and addStyleTag() return nothing, and waitForSelector() and waitForFunction() return frame summaries, not handles; page.mainFrame() returns a frame summary. The same commands on a Frame:class return handles.
    • waitForFunction: pass the predicate as a string expression (page.waitForFunction("window.ready === true")); a function predicate is not supported on page.waitForFunction() (it resolves at once), use page.frames()[0].waitForFunction(fn, options, ...args) instead.
    • content(): returns the document without its DOCTYPE.
    • setUserAgent(): the second argument (userAgentMetadata) is ignored; pass { userAgent, userAgentMetadata } as one object instead.
    • authenticate(): on a render whose proxy has a login (pjsc's built-in proxy, the geo proxy, or a custom proxy with auth), the credentials answer only the website's login prompts and the proxy's login stays; on other renders they answer every login prompt, as in Puppeteer. null clears them.
    • Request and response objects have no body reads (text(), json(), buffer()) and no request interception methods.
    • signal options are not supported (an AbortSignal cannot cross into your overseerScript); waits, locator actions and predicate waits end with the render instead.
    • page.mouse.drag() with drag interception off never settles (as in Puppeteer); the render then ends at maxWait. Call page.setDragInterception(true) first.
    • page.coverage never reports pjsc's own scripts on your page.
    • Protected page functions: the file-picker functions pjsc provides on your page (showOpenFilePicker, showSaveFilePicker, showDirectoryPicker) can be neither exposed nor removed.

    These Puppeteer features are intentionally not provided:

    • Request interception: setRequestInterception, request.abort() / continue() / respond(). Use the request settings of the standard API (requestSettings.resourceModifier) instead.
    • Raw browser protocol (CDP): createCDPSession and similar.
    • The browser, its contexts and targets: page.browser(), browserContext(), target().
    • New pages: browser.newPage(), popups as page objects, page.close().
    • Server file paths: uploadFile, path options that read or write a file on the server (screenshots return base64 text), drag data with files.

    Where to look next

    The best docs to start diving in is the Page documentation.

    Troubleshooting

    • choose either outputAsJson:true or set your renderType:automation to check for automation errors.
    • make sure that any commented lines in your overseerScript end with a newline character (\n)
    • make sure that you use the await keyword to wait for a command to finish before executing the next line.
    • By default the API call will not render until your overseerScript completes. You can call page.done() to signal your script is done earlier.
    • try using page.meta.log("here") to check how far in your script you execute, and use the log function to output debug state for troubleshooting
    • email your script to support@PhantomJsCloud.com and we'll help as soon as possible.

    await page.waitForNavigation(); will wait forever if no page navigation occurs after it is called.

    For example, this will stall:

     //navigate to example.com and wait till done
    await page.goto("http://www.example.com");
    //wait for a navigation to complete
    await page.waitForNavigation();

    but this will not stall, because the navigation doesn't occur until after waitForNavigation() is called:

     //start navigating to example.com and continue script execution immediately
    page.goto("http://www.example.com");
    //wait for a navigation to complete
    await page.waitForNavigation();
    • make sure you don't include a pageRequest.url property if your overseerScript uses page.goto()
    • make sure you use await in your overseerScript when you need to wait for something to occur.
    • try adding page.manualWait() to control when the page finishes, and make sure you call page.done() when ready.

    There are a few ways to obtain error details from your API Response

    • HTTP Response StatusCode: a 400+ status code represents an error with your request or the target server. a 500+ status code is an error with the PhantomJsCloud API. See the Basic Troubleshooting section of the doc page for more info.
    • HTTP Response Headers: we try to summarize high-level information in the http response headers, so check here might give a summary of what an issue is.
    • Automation Errors: any errors encountered by the automation script will be under the pageResponse.automation.errors node.
    • Other Errors: other errors encountered wll be under pageResponse.errors though you may need to look under pageResponses.events and/or meta.trace to see error details.

    Open Source Notice