I know I can do a one-click download for a report using "?export=1&enc=UTF-8&xf=xls" (thanks to Daniel Ballinger for sharing this here ), but does anyone know how I can download the "Printable View" version of a report using this?


You can use this generic apex class for all format

   /*  Allows programmatic retrieve of data generated by any report for further processing in Apex or attaching to emails.
    Uses Page.getContent() by default but in future/scheduled context will fall back to REST GET (remember to add "Remote Site Setting" if you plan to use it like that).

    Use with care, you might still hit heap size limits with bigger reports!
    Of course it can fail anytime Salesforce changes the layout of report pages, their output or parameters that can be passed via URL etc.
// TODO: add support for query string pv0 etc. params for runtime filtering?
public with sharing class ReportExportController {
    public static final Set<String> validEncodings = new Set<String>{'UTF-8','MS932','Shift_JIS','GB18030','GB2312','Big5','EUC_KR','UTF-16','Big5-HKSCS','x-SJIS_0213'};
    public static final Set<String> validformats = new Set<String>{'printable','csv','xls'};
    public static final Set<String> validShowDetails = new Set<String>{'yes','no',null};

    public static String mockOutput; // to be used in unit test. getContent() can't be called in test, 
    // REST way means need to make a mock anyway (but then you'd need remote site setting for both production and all possible sandboxes...)

    // Basic options.
    public Id reportId {get;set;}
    public String format {get;set;} // See list above for available options

    public Boolean prettify {get;set;} /* If set we'll attempt to return only the core info without:
        - headers: who run it, when, which company, what's the report title, filters used
        - footers: "Confidential Information - Do Not Distribute" etc.

    // Option for format = 'printable' only
    public String showDetails {get;set;} // like "show details" button. See list above for available options; null = fallback to however the report was saved

    // Option for format 'csv' and 'xls'
    public String encoding {get;set;} // See list above for available options

    private String requestUrl, output;
    private Boolean restRequestNeeded;

    public ReportExportController(){
        prettify = true;
        restRequestNeeded = System.isFuture() || System.isScheduled(); // System.isBatch() too?
    public ReportExportController(string Prettify,string ReportId, string showDetails,string format){
        this.prettify = Prettify;
        this.ReportId = ReportId;
        this.showDetails = showDetails;
        this.format = format;
        restRequestNeeded = System.isFuture() || System.isScheduled(); // System.isBatch() too?

    public String getOutput(){
        if(requestUrl == null) {
        if(prettify && output != null && output.length() > 0){
        return output;

    public String getRequestUrl(){
        requestUrl = '/' + reportId + '?';
        if(format == 'printable'){
            requestUrl += 'excel=1' + (showDetails != null ? '&details=' + showDetails : '');
        } else {
            requestUrl += 'export=1&xf=' + format + '&enc=' + encoding;
        if(restRequestNeeded) {
            requestUrl = URL.getSalesforceBaseUrl().toExternalForm().replace('http:', 'https:') + requestUrl;
        return requestUrl;

    private void sanitizeInput(){
        if(reportId == null){
            throw new ReportExportException('Missing argument: reportId');
        } else  if(!String.valueOf(reportId).startsWith('00O')) {
            // Length - based check can be skipped - it will fail on attempt to assign too long string to Id variable
            throw new ReportExportException('Suspicious reportId: ' + reportId + '. Were you trying to export something other than a report?');
        if(encoding == null || !validEncodings.contains(encoding)){
            encoding = 'UTF-8';
        if(format == null || !validformats.contains(format)){
            format = 'xls';
            showDetails = null;

    private void fetchData(){
            output = mockOutput;
        } else  if(restRequestNeeded){
            HttpRequest req = new HttpRequest();
            req.setHeader('Cookie','sid=' + UserInfo.getSessionId());
            output = new Http().send(req).getBody();
        } else {
            output = new PageReference(requestUrl).getContent().toString();

    private void prettify(){
        Integer startIndex = 0;
        Integer stopIndex = output.length();

        if(format == 'csv'){
            stopIndex = output.lastIndexOf('\n\n\n');   // "lastindexOf" because we look for last occurence of 3 x "Enter". So it should work even if there's data in report (textarea?) that contains such line
        } else {
            String endToken = '';
            if(format == 'xls'){
                startIndex = output.indexOf('<table>');
                endToken = '</table>';
            } else if(format == 'printable'){
                startIndex = output.indexOf('<div id="fchArea">');
                endToken = '</table>\n</div>';
            stopIndex = output.lastIndexOf(endToken) + endToken.length();
        if(startIndex != -1 && stopIndex != -1) {
            output = output.substring(startIndex, stopIndex);

    public class ReportExportException extends Exception{}

You can call this apex through the custom print button and during instantiate pass required parameter in this way:

ReportExportController obj = new ReportExportController(true,'XXXXXXXXXX Report Id',true,'printable'); 
| improve this answer | |
  • Thank you very much Pandey! – Josephine May 5 at 16:22
  • at request URL, we can also apply filter p0= sth to dynamic filter the report on the fly. – Wayne Ni Sep 15 at 0:09

Try the following: /servlet/PrintableViewDownloadServlet?isdtp=p1&reportId=<YOUR REPORT ID>

This is the modern "magic" URL for exporting; it produces a formatted report that is nearly identical to Classic's "Printable View." This URL produces the report format generated by clicking Export > Formatted Report from a Report in Lightning.

According to Salesforce's response on this Idea, "Formatted Excel is our new version of printable view."

| improve this answer | |
  • Hi Brent, this works beautifully thank you very much! – Josephine May 5 at 16:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.